/*
 * Copyright 2016 WebAssembly Community Group participants
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "binary-reader-interpreter.h"

#include <cassert>
#include <cinttypes>
#include <cstdarg>
#include <cstdio>
#include <vector>

#include "binary-error-handler.h"
#include "binary-reader-nop.h"
#include "interpreter.h"
#include "type-checker.h"
#include "writer.h"

#define CHECK_RESULT(expr)  \
  do {                      \
    if (WABT_FAILED(expr))  \
      return Result::Error; \
  } while (0)

namespace wabt {

namespace {

typedef std::vector<uint32_t> Uint32Vector;
typedef std::vector<Uint32Vector> Uint32VectorVector;

struct Label {
  Label(uint32_t offset, uint32_t fixup_offset);

  uint32_t offset; /* branch location in the istream */
  uint32_t fixup_offset;
};

Label::Label(uint32_t offset, uint32_t fixup_offset)
    : offset(offset), fixup_offset(fixup_offset) {}

struct ElemSegmentInfo {
  ElemSegmentInfo(uint32_t* dst, uint32_t func_index)
      : dst(dst), func_index(func_index) {}

  uint32_t* dst;
  uint32_t func_index;
};

struct DataSegmentInfo {
  DataSegmentInfo(void* dst_data, const void* src_data, uint32_t size)
      : dst_data(dst_data), src_data(src_data), size(size) {}

  void* dst_data;        // Not owned.
  const void* src_data;  // Not owned.
  uint32_t size;
};

class BinaryReaderInterpreter : public BinaryReaderNop {
 public:
  BinaryReaderInterpreter(InterpreterEnvironment* env,
                          DefinedInterpreterModule* module,
                          size_t istream_offset,
                          BinaryErrorHandler* error_handler);

  std::unique_ptr<OutputBuffer> ReleaseOutputBuffer();
  size_t get_istream_offset() { return istream_offset; }

  // Implement BinaryReader.
  virtual bool OnError(const char* message);

  virtual Result EndModule();

  virtual Result OnTypeCount(uint32_t count);
  virtual Result OnType(uint32_t index,
                        uint32_t param_count,
                        Type* param_types,
                        uint32_t result_count,
                        Type* result_types);

  virtual Result OnImportCount(uint32_t count);
  virtual Result OnImport(uint32_t index,
                          StringSlice module_name,
                          StringSlice field_name);
  virtual Result OnImportFunc(uint32_t import_index,
                              StringSlice module_name,
                              StringSlice field_name,
                              uint32_t func_index,
                              uint32_t sig_index);
  virtual Result OnImportTable(uint32_t import_index,
                               StringSlice module_name,
                               StringSlice field_name,
                               uint32_t table_index,
                               Type elem_type,
                               const Limits* elem_limits);
  virtual Result OnImportMemory(uint32_t import_index,
                                StringSlice module_name,
                                StringSlice field_name,
                                uint32_t memory_index,
                                const Limits* page_limits);
  virtual Result OnImportGlobal(uint32_t import_index,
                                StringSlice module_name,
                                StringSlice field_name,
                                uint32_t global_index,
                                Type type,
                                bool mutable_);

  virtual Result OnFunctionCount(uint32_t count);
  virtual Result OnFunction(uint32_t index, uint32_t sig_index);

  virtual Result OnTable(uint32_t index,
                         Type elem_type,
                         const Limits* elem_limits);

  virtual Result OnMemory(uint32_t index, const Limits* limits);

  virtual Result OnGlobalCount(uint32_t count);
  virtual Result BeginGlobal(uint32_t index, Type type, bool mutable_);
  virtual Result EndGlobalInitExpr(uint32_t index);

  virtual Result OnExport(uint32_t index,
                          ExternalKind kind,
                          uint32_t item_index,
                          StringSlice name);

  virtual Result OnStartFunction(uint32_t func_index);

  virtual Result BeginFunctionBody(uint32_t index);
  virtual Result OnLocalDeclCount(uint32_t count);
  virtual Result OnLocalDecl(uint32_t decl_index, uint32_t count, Type type);

  virtual Result OnBinaryExpr(Opcode opcode);
  virtual Result OnBlockExpr(uint32_t num_types, Type* sig_types);
  virtual Result OnBrExpr(uint32_t depth);
  virtual Result OnBrIfExpr(uint32_t depth);
  virtual Result OnBrTableExpr(uint32_t num_targets,
                               uint32_t* target_depths,
                               uint32_t default_target_depth);
  virtual Result OnCallExpr(uint32_t func_index);
  virtual Result OnCallIndirectExpr(uint32_t sig_index);
  virtual Result OnCompareExpr(Opcode opcode);
  virtual Result OnConvertExpr(Opcode opcode);
  virtual Result OnCurrentMemoryExpr();
  virtual Result OnDropExpr();
  virtual Result OnElseExpr();
  virtual Result OnEndExpr();
  virtual Result OnF32ConstExpr(uint32_t value_bits);
  virtual Result OnF64ConstExpr(uint64_t value_bits);
  virtual Result OnGetGlobalExpr(uint32_t global_index);
  virtual Result OnGetLocalExpr(uint32_t local_index);
  virtual Result OnGrowMemoryExpr();
  virtual Result OnI32ConstExpr(uint32_t value);
  virtual Result OnI64ConstExpr(uint64_t value);
  virtual Result OnIfExpr(uint32_t num_types, Type* sig_types);
  virtual Result OnLoadExpr(Opcode opcode,
                            uint32_t alignment_log2,
                            uint32_t offset);
  virtual Result OnLoopExpr(uint32_t num_types, Type* sig_types);
  virtual Result OnNopExpr();
  virtual Result OnReturnExpr();
  virtual Result OnSelectExpr();
  virtual Result OnSetGlobalExpr(uint32_t global_index);
  virtual Result OnSetLocalExpr(uint32_t local_index);
  virtual Result OnStoreExpr(Opcode opcode,
                             uint32_t alignment_log2,
                             uint32_t offset);
  virtual Result OnTeeLocalExpr(uint32_t local_index);
  virtual Result OnUnaryExpr(Opcode opcode);
  virtual Result OnUnreachableExpr();
  virtual Result EndFunctionBody(uint32_t index);

  virtual Result EndElemSegmentInitExpr(uint32_t index);
  virtual Result OnElemSegmentFunctionIndex(uint32_t index,
                                            uint32_t func_index);

  virtual Result OnDataSegmentData(uint32_t index,
                                   const void* data,
                                   uint32_t size);

  virtual Result OnInitExprF32ConstExpr(uint32_t index, uint32_t value);
  virtual Result OnInitExprF64ConstExpr(uint32_t index, uint64_t value);
  virtual Result OnInitExprGetGlobalExpr(uint32_t index, uint32_t global_index);
  virtual Result OnInitExprI32ConstExpr(uint32_t index, uint32_t value);
  virtual Result OnInitExprI64ConstExpr(uint32_t index, uint64_t value);

 private:
  Label* GetLabel(uint32_t depth);
  Label* TopLabel();
  void PushLabel(uint32_t offset, uint32_t fixup_offset);
  void PopLabel();

  bool HandleError(uint32_t offset, const char* message);
  void PrintError(const char* format, ...);
  static void OnTypecheckerError(const char* msg, void* user_data);

  uint32_t TranslateSigIndexToEnv(uint32_t sig_index);
  InterpreterFuncSignature* GetSignatureByEnvIndex(uint32_t sig_index);
  InterpreterFuncSignature* GetSignatureByModuleIndex(uint32_t sig_index);
  uint32_t TranslateFuncIndexToEnv(uint32_t func_index);
  uint32_t TranslateModuleFuncIndexToDefined(uint32_t func_index);
  InterpreterFunc* GetFuncByEnvIndex(uint32_t func_index);
  InterpreterFunc* GetFuncByModuleIndex(uint32_t func_index);
  uint32_t TranslateGlobalIndexToEnv(uint32_t global_index);
  InterpreterGlobal* GetGlobalByEnvIndex(uint32_t global_index);
  InterpreterGlobal* GetGlobalByModuleIndex(uint32_t global_index);
  Type GetGlobalTypeByModuleIndex(uint32_t global_index);
  uint32_t TranslateLocalIndex(uint32_t local_index);
  Type GetLocalTypeByIndex(InterpreterFunc* func, uint32_t local_index);

  uint32_t GetIstreamOffset();

  Result EmitDataAt(size_t offset, const void* data, size_t size);
  Result EmitData(const void* data, size_t size);
  Result EmitOpcode(Opcode opcode);
  Result EmitOpcode(InterpreterOpcode opcode);
  Result EmitI8(uint8_t value);
  Result EmitI32(uint32_t value);
  Result EmitI64(uint64_t value);
  Result EmitI32At(uint32_t offset, uint32_t value);
  Result EmitDropKeep(uint32_t drop, uint8_t keep);
  Result AppendFixup(Uint32VectorVector* fixups_vector, uint32_t index);
  Result EmitBrOffset(uint32_t depth, uint32_t offset);
  Result GetBrDropKeepCount(uint32_t depth,
                            uint32_t* out_drop_count,
                            uint32_t* out_keep_count);
  Result GetReturnDropKeepCount(uint32_t* out_drop_count,
                                uint32_t* out_keep_count);
  Result EmitBr(uint32_t depth, uint32_t drop_count, uint32_t keep_count);
  Result EmitBrTableOffset(uint32_t depth);
  Result FixupTopLabel();
  Result EmitFuncOffset(DefinedInterpreterFunc* func, uint32_t func_index);

  Result CheckLocal(uint32_t local_index);
  Result CheckGlobal(uint32_t global_index);
  Result CheckImportKind(InterpreterImport* import, ExternalKind expected_kind);
  Result CheckImportLimits(const Limits* declared_limits,
                           const Limits* actual_limits);
  Result CheckHasMemory(Opcode opcode);
  Result CheckAlign(uint32_t alignment_log2, uint32_t natural_alignment);

  Result AppendExport(InterpreterModule* module,
                      ExternalKind kind,
                      uint32_t item_index,
                      StringSlice name);

  PrintErrorCallback MakePrintErrorCallback();
  static void OnHostImportPrintError(const char* msg, void* user_data);

  BinaryErrorHandler* error_handler = nullptr;
  InterpreterEnvironment* env = nullptr;
  DefinedInterpreterModule* module = nullptr;
  DefinedInterpreterFunc* current_func = nullptr;
  TypeCheckerErrorHandler tc_error_handler;
  TypeChecker typechecker;
  std::vector<Label> label_stack;
  Uint32VectorVector func_fixups;
  Uint32VectorVector depth_fixups;
  MemoryWriter istream_writer;
  uint32_t istream_offset = 0;
  /* mappings from module index space to env index space; this won't just be a
   * translation, because imported values will be resolved as well */
  Uint32Vector sig_index_mapping;
  Uint32Vector func_index_mapping;
  Uint32Vector global_index_mapping;

  uint32_t num_func_imports = 0;
  uint32_t num_global_imports = 0;

  // Changes to linear memory and tables should not apply if a validation error
  // occurs; these vectors cache the changes that must be applied after we know
  // that there are no validation errors.
  std::vector<ElemSegmentInfo> elem_segment_infos;
  std::vector<DataSegmentInfo> data_segment_infos;

  /* values cached so they can be shared between callbacks */
  InterpreterTypedValue init_expr_value;
  uint32_t table_offset = 0;
  bool is_host_import = false;
  HostInterpreterModule* host_import_module = nullptr;
  uint32_t import_env_index = 0;
};

BinaryReaderInterpreter::BinaryReaderInterpreter(
    InterpreterEnvironment* env,
    DefinedInterpreterModule* module,
    size_t istream_offset,
    BinaryErrorHandler* error_handler)
    : error_handler(error_handler),
      env(env),
      module(module),
      istream_writer(std::move(env->istream)),
      istream_offset(istream_offset) {
  tc_error_handler.on_error = OnTypecheckerError;
  tc_error_handler.user_data = this;
  typechecker.error_handler = &tc_error_handler;
}

std::unique_ptr<OutputBuffer> BinaryReaderInterpreter::ReleaseOutputBuffer() {
  return istream_writer.ReleaseOutputBuffer();
}

Label* BinaryReaderInterpreter::GetLabel(uint32_t depth) {
  assert(depth < label_stack.size());
  return &label_stack[label_stack.size() - depth - 1];
}

Label* BinaryReaderInterpreter::TopLabel() {
  return GetLabel(0);
}

bool BinaryReaderInterpreter::HandleError(uint32_t offset,
                                          const char* message) {
  return error_handler->OnError(offset, message);
}

void WABT_PRINTF_FORMAT(2, 3)
    BinaryReaderInterpreter::PrintError(const char* format, ...) {
  WABT_SNPRINTF_ALLOCA(buffer, length, format);
  HandleError(WABT_INVALID_OFFSET, buffer);
}

// static
void BinaryReaderInterpreter::OnTypecheckerError(const char* msg,
                                                 void* user_data) {
  static_cast<BinaryReaderInterpreter*>(user_data)->PrintError("%s", msg);
}

uint32_t BinaryReaderInterpreter::TranslateSigIndexToEnv(uint32_t sig_index) {
  assert(sig_index < sig_index_mapping.size());
  return sig_index_mapping[sig_index];
}

InterpreterFuncSignature* BinaryReaderInterpreter::GetSignatureByEnvIndex(
    uint32_t sig_index) {
  return &env->sigs[sig_index];
}

InterpreterFuncSignature* BinaryReaderInterpreter::GetSignatureByModuleIndex(
    uint32_t sig_index) {
  return GetSignatureByEnvIndex(TranslateSigIndexToEnv(sig_index));
}

uint32_t BinaryReaderInterpreter::TranslateFuncIndexToEnv(uint32_t func_index) {
  assert(func_index < func_index_mapping.size());
  return func_index_mapping[func_index];
}

uint32_t BinaryReaderInterpreter::TranslateModuleFuncIndexToDefined(
    uint32_t func_index) {
  assert(func_index >= num_func_imports);
  return func_index - num_func_imports;
}

InterpreterFunc* BinaryReaderInterpreter::GetFuncByEnvIndex(
    uint32_t func_index) {
  return env->funcs[func_index].get();
}

InterpreterFunc* BinaryReaderInterpreter::GetFuncByModuleIndex(
    uint32_t func_index) {
  return GetFuncByEnvIndex(TranslateFuncIndexToEnv(func_index));
}

uint32_t BinaryReaderInterpreter::TranslateGlobalIndexToEnv(
    uint32_t global_index) {
  return global_index_mapping[global_index];
}

InterpreterGlobal* BinaryReaderInterpreter::GetGlobalByEnvIndex(
    uint32_t global_index) {
  return &env->globals[global_index];
}

InterpreterGlobal* BinaryReaderInterpreter::GetGlobalByModuleIndex(
    uint32_t global_index) {
  return GetGlobalByEnvIndex(TranslateGlobalIndexToEnv(global_index));
}

Type BinaryReaderInterpreter::GetGlobalTypeByModuleIndex(
    uint32_t global_index) {
  return GetGlobalByModuleIndex(global_index)->typed_value.type;
}

Type BinaryReaderInterpreter::GetLocalTypeByIndex(InterpreterFunc* func,
                                                  uint32_t local_index) {
  assert(!func->is_host);
  return func->as_defined()->param_and_local_types[local_index];
}

uint32_t BinaryReaderInterpreter::GetIstreamOffset() {
  return istream_offset;
}

Result BinaryReaderInterpreter::EmitDataAt(size_t offset,
                                           const void* data,
                                           size_t size) {
  return istream_writer.WriteData(offset, data, size);
}

Result BinaryReaderInterpreter::EmitData(const void* data, size_t size) {
  CHECK_RESULT(EmitDataAt(istream_offset, data, size));
  istream_offset += size;
  return Result::Ok;
}

Result BinaryReaderInterpreter::EmitOpcode(Opcode opcode) {
  return EmitData(&opcode, sizeof(uint8_t));
}

Result BinaryReaderInterpreter::EmitOpcode(InterpreterOpcode opcode) {
  return EmitData(&opcode, sizeof(uint8_t));
}

Result BinaryReaderInterpreter::EmitI8(uint8_t value) {
  return EmitData(&value, sizeof(value));
}

Result BinaryReaderInterpreter::EmitI32(uint32_t value) {
  return EmitData(&value, sizeof(value));
}

Result BinaryReaderInterpreter::EmitI64(uint64_t value) {
  return EmitData(&value, sizeof(value));
}

Result BinaryReaderInterpreter::EmitI32At(uint32_t offset, uint32_t value) {
  return EmitDataAt(offset, &value, sizeof(value));
}

Result BinaryReaderInterpreter::EmitDropKeep(uint32_t drop, uint8_t keep) {
  assert(drop != UINT32_MAX);
  assert(keep <= 1);
  if (drop > 0) {
    if (drop == 1 && keep == 0) {
      CHECK_RESULT(EmitOpcode(InterpreterOpcode::Drop));
    } else {
      CHECK_RESULT(EmitOpcode(InterpreterOpcode::DropKeep));
      CHECK_RESULT(EmitI32(drop));
      CHECK_RESULT(EmitI8(keep));
    }
  }
  return Result::Ok;
}

Result BinaryReaderInterpreter::AppendFixup(Uint32VectorVector* fixups_vector,
                                            uint32_t index) {
  if (index >= fixups_vector->size())
    fixups_vector->resize(index + 1);
  (*fixups_vector)[index].push_back(GetIstreamOffset());
  return Result::Ok;
}

Result BinaryReaderInterpreter::EmitBrOffset(uint32_t depth, uint32_t offset) {
  if (offset == WABT_INVALID_OFFSET) {
    /* depth_fixups stores the depth counting up from zero, where zero is the
     * top-level function scope. */
    depth = label_stack.size() - 1 - depth;
    CHECK_RESULT(AppendFixup(&depth_fixups, depth));
  }
  CHECK_RESULT(EmitI32(offset));
  return Result::Ok;
}

Result BinaryReaderInterpreter::GetBrDropKeepCount(uint32_t depth,
                                                   uint32_t* out_drop_count,
                                                   uint32_t* out_keep_count) {
  TypeCheckerLabel* label;
  CHECK_RESULT(typechecker_get_label(&typechecker, depth, &label));
  *out_keep_count =
      label->label_type != LabelType::Loop ? label->sig.size() : 0;
  if (typechecker_is_unreachable(&typechecker)) {
    *out_drop_count = 0;
  } else {
    *out_drop_count =
        (typechecker.type_stack.size() - label->type_stack_limit) -
        *out_keep_count;
  }
  return Result::Ok;
}

Result BinaryReaderInterpreter::GetReturnDropKeepCount(
    uint32_t* out_drop_count,
    uint32_t* out_keep_count) {
  if (WABT_FAILED(GetBrDropKeepCount(label_stack.size() - 1, out_drop_count,
                                     out_keep_count))) {
    return Result::Error;
  }

  *out_drop_count += current_func->param_and_local_types.size();
  return Result::Ok;
}

Result BinaryReaderInterpreter::EmitBr(uint32_t depth,
                                       uint32_t drop_count,
                                       uint32_t keep_count) {
  CHECK_RESULT(EmitDropKeep(drop_count, keep_count));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::Br));
  CHECK_RESULT(EmitBrOffset(depth, GetLabel(depth)->offset));
  return Result::Ok;
}

Result BinaryReaderInterpreter::EmitBrTableOffset(uint32_t depth) {
  uint32_t drop_count, keep_count;
  CHECK_RESULT(GetBrDropKeepCount(depth, &drop_count, &keep_count));
  CHECK_RESULT(EmitBrOffset(depth, GetLabel(depth)->offset));
  CHECK_RESULT(EmitI32(drop_count));
  CHECK_RESULT(EmitI8(keep_count));
  return Result::Ok;
}

Result BinaryReaderInterpreter::FixupTopLabel() {
  uint32_t offset = GetIstreamOffset();
  uint32_t top = label_stack.size() - 1;
  if (top >= depth_fixups.size()) {
    /* nothing to fixup */
    return Result::Ok;
  }

  Uint32Vector& fixups = depth_fixups[top];
  for (uint32_t fixup : fixups)
    CHECK_RESULT(EmitI32At(fixup, offset));
  fixups.clear();
  return Result::Ok;
}

Result BinaryReaderInterpreter::EmitFuncOffset(DefinedInterpreterFunc* func,
                                               uint32_t func_index) {
  if (func->offset == WABT_INVALID_OFFSET) {
    uint32_t defined_index = TranslateModuleFuncIndexToDefined(func_index);
    CHECK_RESULT(AppendFixup(&func_fixups, defined_index));
  }
  CHECK_RESULT(EmitI32(func->offset));
  return Result::Ok;
}

bool BinaryReaderInterpreter::OnError(const char* message) {
  return HandleError(state->offset, message);
}

Result BinaryReaderInterpreter::OnTypeCount(uint32_t count) {
  sig_index_mapping.resize(count);
  for (uint32_t i = 0; i < count; ++i)
    sig_index_mapping[i] = env->sigs.size() + i;
  env->sigs.resize(env->sigs.size() + count);
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnType(uint32_t index,
                                       uint32_t param_count,
                                       Type* param_types,
                                       uint32_t result_count,
                                       Type* result_types) {
  InterpreterFuncSignature* sig = GetSignatureByModuleIndex(index);
  sig->param_types.insert(sig->param_types.end(), param_types,
                          param_types + param_count);
  sig->result_types.insert(sig->result_types.end(), result_types,
                           result_types + result_count);
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnImportCount(uint32_t count) {
  module->imports.resize(count);
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnImport(uint32_t index,
                                         StringSlice module_name,
                                         StringSlice field_name) {
  InterpreterImport* import = &module->imports[index];
  import->module_name = dup_string_slice(module_name);
  import->field_name = dup_string_slice(field_name);
  int module_index =
      env->registered_module_bindings.find_index(import->module_name);
  if (module_index < 0) {
    PrintError("unknown import module \"" PRIstringslice "\"",
               WABT_PRINTF_STRING_SLICE_ARG(import->module_name));
    return Result::Error;
  }

  InterpreterModule* module = env->modules[module_index].get();
  if (module->is_host) {
    /* We don't yet know the kind of a host import module, so just assume it
     * exists for now. We'll fail later (in on_import_* below) if it doesn't
     * exist). */
    is_host_import = true;
    host_import_module = module->as_host();
  } else {
    InterpreterExport* export_ =
        get_interpreter_export_by_name(module, &import->field_name);
    if (!export_) {
      PrintError("unknown module field \"" PRIstringslice "\"",
                 WABT_PRINTF_STRING_SLICE_ARG(import->field_name));
      return Result::Error;
    }

    import->kind = export_->kind;
    is_host_import = false;
    import_env_index = export_->index;
  }
  return Result::Ok;
}

Result BinaryReaderInterpreter::CheckLocal(uint32_t local_index) {
  uint32_t max_local_index = current_func->param_and_local_types.size();
  if (local_index >= max_local_index) {
    PrintError("invalid local_index: %d (max %d)", local_index,
               max_local_index);
    return Result::Error;
  }
  return Result::Ok;
}

Result BinaryReaderInterpreter::CheckGlobal(uint32_t global_index) {
  uint32_t max_global_index = global_index_mapping.size();
  if (global_index >= max_global_index) {
    PrintError("invalid global_index: %d (max %d)", global_index,
               max_global_index);
    return Result::Error;
  }
  return Result::Ok;
}

Result BinaryReaderInterpreter::CheckImportKind(InterpreterImport* import,
                                                ExternalKind expected_kind) {
  if (import->kind != expected_kind) {
    PrintError("expected import \"" PRIstringslice "." PRIstringslice
               "\" to have kind %s, not %s",
               WABT_PRINTF_STRING_SLICE_ARG(import->module_name),
               WABT_PRINTF_STRING_SLICE_ARG(import->field_name),
               get_kind_name(expected_kind), get_kind_name(import->kind));
    return Result::Error;
  }
  return Result::Ok;
}

Result BinaryReaderInterpreter::CheckImportLimits(const Limits* declared_limits,
                                                  const Limits* actual_limits) {
  if (actual_limits->initial < declared_limits->initial) {
    PrintError("actual size (%" PRIu64 ") smaller than declared (%" PRIu64 ")",
               actual_limits->initial, declared_limits->initial);
    return Result::Error;
  }

  if (declared_limits->has_max) {
    if (!actual_limits->has_max) {
      PrintError("max size (unspecified) larger than declared (%" PRIu64 ")",
                 declared_limits->max);
      return Result::Error;
    } else if (actual_limits->max > declared_limits->max) {
      PrintError("max size (%" PRIu64 ") larger than declared (%" PRIu64 ")",
                 actual_limits->max, declared_limits->max);
      return Result::Error;
    }
  }

  return Result::Ok;
}

Result BinaryReaderInterpreter::AppendExport(InterpreterModule* module,
                                             ExternalKind kind,
                                             uint32_t item_index,
                                             StringSlice name) {
  if (module->export_bindings.find_index(name) != -1) {
    PrintError("duplicate export \"" PRIstringslice "\"",
               WABT_PRINTF_STRING_SLICE_ARG(name));
    return Result::Error;
  }

  module->exports.emplace_back(dup_string_slice(name), kind, item_index);
  InterpreterExport* export_ = &module->exports.back();

  module->export_bindings.emplace(string_slice_to_string(export_->name),
                                  Binding(module->exports.size() - 1));
  return Result::Ok;
}

// static
void BinaryReaderInterpreter::OnHostImportPrintError(const char* msg,
                                                     void* user_data) {
  static_cast<BinaryReaderInterpreter*>(user_data)->PrintError("%s", msg);
}

PrintErrorCallback BinaryReaderInterpreter::MakePrintErrorCallback() {
  PrintErrorCallback result;
  result.print_error = OnHostImportPrintError;
  result.user_data = this;
  return result;
}

Result BinaryReaderInterpreter::OnImportFunc(uint32_t import_index,
                                             StringSlice module_name,
                                             StringSlice field_name,
                                             uint32_t func_index,
                                             uint32_t sig_index) {
  InterpreterImport* import = &module->imports[import_index];
  import->func.sig_index = TranslateSigIndexToEnv(sig_index);

  uint32_t func_env_index;
  if (is_host_import) {
    HostInterpreterFunc* func = new HostInterpreterFunc(
        import->module_name, import->field_name, import->func.sig_index);

    env->funcs.emplace_back(func);

    InterpreterHostImportDelegate* host_delegate =
        &host_import_module->import_delegate;
    InterpreterFuncSignature* sig = &env->sigs[func->sig_index];
    CHECK_RESULT(host_delegate->import_func(
        import, func, sig, MakePrintErrorCallback(), host_delegate->user_data));
    assert(func->callback);

    func_env_index = env->funcs.size() - 1;
    AppendExport(host_import_module, ExternalKind::Func, func_env_index,
                 import->field_name);
  } else {
    CHECK_RESULT(CheckImportKind(import, ExternalKind::Func));
    InterpreterFunc* func = env->funcs[import_env_index].get();
    if (!func_signatures_are_equal(env, import->func.sig_index,
                                   func->sig_index)) {
      PrintError("import signature mismatch");
      return Result::Error;
    }

    func_env_index = import_env_index;
  }
  func_index_mapping.push_back(func_env_index);
  num_func_imports++;
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnImportTable(uint32_t import_index,
                                              StringSlice module_name,
                                              StringSlice field_name,
                                              uint32_t table_index,
                                              Type elem_type,
                                              const Limits* elem_limits) {
  if (module->table_index != WABT_INVALID_INDEX) {
    PrintError("only one table allowed");
    return Result::Error;
  }

  InterpreterImport* import = &module->imports[import_index];

  if (is_host_import) {
    env->tables.emplace_back(*elem_limits);
    InterpreterTable* table = &env->tables.back();

    InterpreterHostImportDelegate* host_delegate =
        &host_import_module->import_delegate;
    CHECK_RESULT(host_delegate->import_table(
        import, table, MakePrintErrorCallback(), host_delegate->user_data));

    CHECK_RESULT(CheckImportLimits(elem_limits, &table->limits));

    module->table_index = env->tables.size() - 1;
    AppendExport(host_import_module, ExternalKind::Table, module->table_index,
                 import->field_name);
  } else {
    CHECK_RESULT(CheckImportKind(import, ExternalKind::Table));
    InterpreterTable* table = &env->tables[import_env_index];
    CHECK_RESULT(CheckImportLimits(elem_limits, &table->limits));

    import->table.limits = *elem_limits;
    module->table_index = import_env_index;
  }
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnImportMemory(uint32_t import_index,
                                               StringSlice module_name,
                                               StringSlice field_name,
                                               uint32_t memory_index,
                                               const Limits* page_limits) {
  if (module->memory_index != WABT_INVALID_INDEX) {
    PrintError("only one memory allowed");
    return Result::Error;
  }

  InterpreterImport* import = &module->imports[import_index];

  if (is_host_import) {
    env->memories.emplace_back();
    InterpreterMemory* memory = &env->memories.back();

    InterpreterHostImportDelegate* host_delegate =
        &host_import_module->import_delegate;
    CHECK_RESULT(host_delegate->import_memory(
        import, memory, MakePrintErrorCallback(), host_delegate->user_data));

    CHECK_RESULT(CheckImportLimits(page_limits, &memory->page_limits));

    module->memory_index = env->memories.size() - 1;
    AppendExport(host_import_module, ExternalKind::Memory, module->memory_index,
                 import->field_name);
  } else {
    CHECK_RESULT(CheckImportKind(import, ExternalKind::Memory));
    InterpreterMemory* memory = &env->memories[import_env_index];
    CHECK_RESULT(CheckImportLimits(page_limits, &memory->page_limits));

    import->memory.limits = *page_limits;
    module->memory_index = import_env_index;
  }
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnImportGlobal(uint32_t import_index,
                                               StringSlice module_name,
                                               StringSlice field_name,
                                               uint32_t global_index,
                                               Type type,
                                               bool mutable_) {
  InterpreterImport* import = &module->imports[import_index];

  uint32_t global_env_index = env->globals.size() - 1;
  if (is_host_import) {
    env->globals.emplace_back(InterpreterTypedValue(type), mutable_);
    InterpreterGlobal* global = &env->globals.back();

    InterpreterHostImportDelegate* host_delegate =
        &host_import_module->import_delegate;
    CHECK_RESULT(host_delegate->import_global(
        import, global, MakePrintErrorCallback(), host_delegate->user_data));

    global_env_index = env->globals.size() - 1;
    AppendExport(host_import_module, ExternalKind::Global, global_env_index,
                 import->field_name);
  } else {
    CHECK_RESULT(CheckImportKind(import, ExternalKind::Global));
    // TODO: check type and mutability
    import->global.type = type;
    import->global.mutable_ = mutable_;
    global_env_index = import_env_index;
  }
  global_index_mapping.push_back(global_env_index);
  num_global_imports++;
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnFunctionCount(uint32_t count) {
  for (uint32_t i = 0; i < count; ++i)
    func_index_mapping.push_back(env->funcs.size() + i);
  env->funcs.reserve(env->funcs.size() + count);
  func_fixups.resize(count);
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnFunction(uint32_t index, uint32_t sig_index) {
  DefinedInterpreterFunc* func =
      new DefinedInterpreterFunc(TranslateSigIndexToEnv(sig_index));
  env->funcs.emplace_back(func);
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnTable(uint32_t index,
                                        Type elem_type,
                                        const Limits* elem_limits) {
  if (module->table_index != WABT_INVALID_INDEX) {
    PrintError("only one table allowed");
    return Result::Error;
  }
  env->tables.emplace_back(*elem_limits);
  module->table_index = env->tables.size() - 1;
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnMemory(uint32_t index,
                                         const Limits* page_limits) {
  if (module->memory_index != WABT_INVALID_INDEX) {
    PrintError("only one memory allowed");
    return Result::Error;
  }
  env->memories.emplace_back(*page_limits);
  module->memory_index = env->memories.size() - 1;
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnGlobalCount(uint32_t count) {
  for (uint32_t i = 0; i < count; ++i)
    global_index_mapping.push_back(env->globals.size() + i);
  env->globals.resize(env->globals.size() + count);
  return Result::Ok;
}

Result BinaryReaderInterpreter::BeginGlobal(uint32_t index,
                                            Type type,
                                            bool mutable_) {
  InterpreterGlobal* global = GetGlobalByModuleIndex(index);
  global->typed_value.type = type;
  global->mutable_ = mutable_;
  init_expr_value.type = Type::Void;
  return Result::Ok;
}

Result BinaryReaderInterpreter::EndGlobalInitExpr(uint32_t index) {
  InterpreterGlobal* global = GetGlobalByModuleIndex(index);
  if (init_expr_value.type != global->typed_value.type) {
    PrintError("type mismatch in global, expected %s but got %s.",
               get_type_name(global->typed_value.type),
               get_type_name(init_expr_value.type));
    return Result::Error;
  }
  global->typed_value = init_expr_value;
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnInitExprF32ConstExpr(uint32_t index,
                                                       uint32_t value_bits) {
  init_expr_value.type = Type::F32;
  init_expr_value.value.f32_bits = value_bits;
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnInitExprF64ConstExpr(uint32_t index,
                                                       uint64_t value_bits) {
  init_expr_value.type = Type::F64;
  init_expr_value.value.f64_bits = value_bits;
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnInitExprGetGlobalExpr(uint32_t index,
                                                        uint32_t global_index) {
  if (global_index >= num_global_imports) {
    PrintError("initializer expression can only reference an imported global");
    return Result::Error;
  }
  InterpreterGlobal* ref_global = GetGlobalByModuleIndex(global_index);
  if (ref_global->mutable_) {
    PrintError("initializer expression cannot reference a mutable global");
    return Result::Error;
  }
  init_expr_value = ref_global->typed_value;
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnInitExprI32ConstExpr(uint32_t index,
                                                       uint32_t value) {
  init_expr_value.type = Type::I32;
  init_expr_value.value.i32 = value;
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnInitExprI64ConstExpr(uint32_t index,
                                                       uint64_t value) {
  init_expr_value.type = Type::I64;
  init_expr_value.value.i64 = value;
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnExport(uint32_t index,
                                         ExternalKind kind,
                                         uint32_t item_index,
                                         StringSlice name) {
  switch (kind) {
    case ExternalKind::Func:
      item_index = TranslateFuncIndexToEnv(item_index);
      break;

    case ExternalKind::Table:
      item_index = module->table_index;
      break;

    case ExternalKind::Memory:
      item_index = module->memory_index;
      break;

    case ExternalKind::Global: {
      item_index = TranslateGlobalIndexToEnv(item_index);
      InterpreterGlobal* global = &env->globals[item_index];
      if (global->mutable_) {
        PrintError("mutable globals cannot be exported");
        return Result::Error;
      }
      break;
    }
  }
  return AppendExport(module, kind, item_index, name);
}

Result BinaryReaderInterpreter::OnStartFunction(uint32_t func_index) {
  uint32_t start_func_index = TranslateFuncIndexToEnv(func_index);
  InterpreterFunc* start_func = GetFuncByEnvIndex(start_func_index);
  InterpreterFuncSignature* sig = GetSignatureByEnvIndex(start_func->sig_index);
  if (sig->param_types.size() != 0) {
    PrintError("start function must be nullary");
    return Result::Error;
  }
  if (sig->result_types.size() != 0) {
    PrintError("start function must not return anything");
    return Result::Error;
  }
  module->start_func_index = start_func_index;
  return Result::Ok;
}

Result BinaryReaderInterpreter::EndElemSegmentInitExpr(uint32_t index) {
  if (init_expr_value.type != Type::I32) {
    PrintError("type mismatch in elem segment, expected i32 but got %s",
               get_type_name(init_expr_value.type));
    return Result::Error;
  }
  table_offset = init_expr_value.value.i32;
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnElemSegmentFunctionIndex(

    uint32_t index,
    uint32_t func_index) {
  assert(module->table_index != WABT_INVALID_INDEX);
  InterpreterTable* table = &env->tables[module->table_index];
  if (table_offset >= table->func_indexes.size()) {
    PrintError("elem segment offset is out of bounds: %u >= max value %" PRIzd,
               table_offset, table->func_indexes.size());
    return Result::Error;
  }

  uint32_t max_func_index = func_index_mapping.size();
  if (func_index >= max_func_index) {
    PrintError("invalid func_index: %d (max %d)", func_index, max_func_index);
    return Result::Error;
  }

  elem_segment_infos.emplace_back(&table->func_indexes[table_offset++],
                                  TranslateFuncIndexToEnv(func_index));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnDataSegmentData(uint32_t index,
                                                  const void* src_data,
                                                  uint32_t size) {
  assert(module->memory_index != WABT_INVALID_INDEX);
  InterpreterMemory* memory = &env->memories[module->memory_index];
  if (init_expr_value.type != Type::I32) {
    PrintError("type mismatch in data segment, expected i32 but got %s",
               get_type_name(init_expr_value.type));
    return Result::Error;
  }
  uint32_t address = init_expr_value.value.i32;
  uint64_t end_address =
      static_cast<uint64_t>(address) + static_cast<uint64_t>(size);
  if (end_address > memory->data.size()) {
    PrintError("data segment is out of bounds: [%u, %" PRIu64
               ") >= max value %" PRIzd,
               address, end_address, memory->data.size());
    return Result::Error;
  }

  if (size > 0)
    data_segment_infos.emplace_back(&memory->data[address], src_data, size);

  return Result::Ok;
}

void BinaryReaderInterpreter::PushLabel(uint32_t offset,
                                        uint32_t fixup_offset) {
  label_stack.emplace_back(offset, fixup_offset);
}

void BinaryReaderInterpreter::PopLabel() {
  label_stack.pop_back();
  /* reduce the depth_fixups stack as well, but it may be smaller than
   * label_stack so only do it conditionally. */
  if (depth_fixups.size() > label_stack.size()) {
    depth_fixups.erase(depth_fixups.begin() + label_stack.size(),
                       depth_fixups.end());
  }
}

Result BinaryReaderInterpreter::BeginFunctionBody(uint32_t index) {
  DefinedInterpreterFunc* func = GetFuncByModuleIndex(index)->as_defined();
  InterpreterFuncSignature* sig = GetSignatureByEnvIndex(func->sig_index);

  func->offset = GetIstreamOffset();
  func->local_decl_count = 0;
  func->local_count = 0;

  current_func = func;
  depth_fixups.clear();
  label_stack.clear();

  /* fixup function references */
  uint32_t defined_index = TranslateModuleFuncIndexToDefined(index);
  Uint32Vector& fixups = func_fixups[defined_index];
  for (uint32_t fixup : fixups)
    CHECK_RESULT(EmitI32At(fixup, func->offset));

  /* append param types */
  for (Type param_type : sig->param_types)
    func->param_and_local_types.push_back(param_type);

  CHECK_RESULT(typechecker_begin_function(&typechecker, &sig->result_types));

  /* push implicit func label (equivalent to return) */
  PushLabel(WABT_INVALID_OFFSET, WABT_INVALID_OFFSET);
  return Result::Ok;
}

Result BinaryReaderInterpreter::EndFunctionBody(uint32_t index) {
  FixupTopLabel();
  uint32_t drop_count, keep_count;
  CHECK_RESULT(GetReturnDropKeepCount(&drop_count, &keep_count));
  CHECK_RESULT(typechecker_end_function(&typechecker));
  CHECK_RESULT(EmitDropKeep(drop_count, keep_count));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::Return));
  PopLabel();
  current_func = nullptr;
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnLocalDeclCount(uint32_t count) {
  current_func->local_decl_count = count;
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnLocalDecl(uint32_t decl_index,
                                            uint32_t count,
                                            Type type) {
  current_func->local_count += count;

  for (uint32_t i = 0; i < count; ++i)
    current_func->param_and_local_types.push_back(type);

  if (decl_index == current_func->local_decl_count - 1) {
    /* last local declaration, allocate space for all locals. */
    CHECK_RESULT(EmitOpcode(InterpreterOpcode::Alloca));
    CHECK_RESULT(EmitI32(current_func->local_count));
  }
  return Result::Ok;
}

Result BinaryReaderInterpreter::CheckHasMemory(Opcode opcode) {
  if (module->memory_index == WABT_INVALID_INDEX) {
    PrintError("%s requires an imported or defined memory.",
               get_opcode_name(opcode));
    return Result::Error;
  }
  return Result::Ok;
}

Result BinaryReaderInterpreter::CheckAlign(uint32_t alignment_log2,
                                           uint32_t natural_alignment) {
  if (alignment_log2 >= 32 || (1U << alignment_log2) > natural_alignment) {
    PrintError("alignment must not be larger than natural alignment (%u)",
               natural_alignment);
    return Result::Error;
  }
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnUnaryExpr(Opcode opcode) {
  CHECK_RESULT(typechecker_on_unary(&typechecker, opcode));
  CHECK_RESULT(EmitOpcode(opcode));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnBinaryExpr(Opcode opcode) {
  CHECK_RESULT(typechecker_on_binary(&typechecker, opcode));
  CHECK_RESULT(EmitOpcode(opcode));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnBlockExpr(uint32_t num_types,
                                            Type* sig_types) {
  TypeVector sig(sig_types, sig_types + num_types);
  CHECK_RESULT(typechecker_on_block(&typechecker, &sig));
  PushLabel(WABT_INVALID_OFFSET, WABT_INVALID_OFFSET);
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnLoopExpr(uint32_t num_types,
                                           Type* sig_types) {
  TypeVector sig(sig_types, sig_types + num_types);
  CHECK_RESULT(typechecker_on_loop(&typechecker, &sig));
  PushLabel(GetIstreamOffset(), WABT_INVALID_OFFSET);
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnIfExpr(uint32_t num_types, Type* sig_types) {
  TypeVector sig(sig_types, sig_types + num_types);
  CHECK_RESULT(typechecker_on_if(&typechecker, &sig));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::BrUnless));
  uint32_t fixup_offset = GetIstreamOffset();
  CHECK_RESULT(EmitI32(WABT_INVALID_OFFSET));
  PushLabel(WABT_INVALID_OFFSET, fixup_offset);
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnElseExpr() {
  CHECK_RESULT(typechecker_on_else(&typechecker));
  Label* label = TopLabel();
  uint32_t fixup_cond_offset = label->fixup_offset;
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::Br));
  label->fixup_offset = GetIstreamOffset();
  CHECK_RESULT(EmitI32(WABT_INVALID_OFFSET));
  CHECK_RESULT(EmitI32At(fixup_cond_offset, GetIstreamOffset()));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnEndExpr() {
  TypeCheckerLabel* label;
  CHECK_RESULT(typechecker_get_label(&typechecker, 0, &label));
  LabelType label_type = label->label_type;
  CHECK_RESULT(typechecker_on_end(&typechecker));
  if (label_type == LabelType::If || label_type == LabelType::Else) {
    CHECK_RESULT(EmitI32At(TopLabel()->fixup_offset, GetIstreamOffset()));
  }
  FixupTopLabel();
  PopLabel();
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnBrExpr(uint32_t depth) {
  uint32_t drop_count, keep_count;
  CHECK_RESULT(GetBrDropKeepCount(depth, &drop_count, &keep_count));
  CHECK_RESULT(typechecker_on_br(&typechecker, depth));
  CHECK_RESULT(EmitBr(depth, drop_count, keep_count));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnBrIfExpr(uint32_t depth) {
  uint32_t drop_count, keep_count;
  CHECK_RESULT(typechecker_on_br_if(&typechecker, depth));
  CHECK_RESULT(GetBrDropKeepCount(depth, &drop_count, &keep_count));
  /* flip the br_if so if <cond> is true it can drop values from the stack */
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::BrUnless));
  uint32_t fixup_br_offset = GetIstreamOffset();
  CHECK_RESULT(EmitI32(WABT_INVALID_OFFSET));
  CHECK_RESULT(EmitBr(depth, drop_count, keep_count));
  CHECK_RESULT(EmitI32At(fixup_br_offset, GetIstreamOffset()));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnBrTableExpr(uint32_t num_targets,
                                              uint32_t* target_depths,
                                              uint32_t default_target_depth) {
  CHECK_RESULT(typechecker_begin_br_table(&typechecker));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::BrTable));
  CHECK_RESULT(EmitI32(num_targets));
  uint32_t fixup_table_offset = GetIstreamOffset();
  CHECK_RESULT(EmitI32(WABT_INVALID_OFFSET));
  /* not necessary for the interpreter, but it makes it easier to disassemble.
   * This opcode specifies how many bytes of data follow. */
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::Data));
  CHECK_RESULT(EmitI32((num_targets + 1) * WABT_TABLE_ENTRY_SIZE));
  CHECK_RESULT(EmitI32At(fixup_table_offset, GetIstreamOffset()));

  for (uint32_t i = 0; i <= num_targets; ++i) {
    uint32_t depth = i != num_targets ? target_depths[i] : default_target_depth;
    CHECK_RESULT(typechecker_on_br_table_target(&typechecker, depth));
    CHECK_RESULT(EmitBrTableOffset(depth));
  }

  CHECK_RESULT(typechecker_end_br_table(&typechecker));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnCallExpr(uint32_t func_index) {
  InterpreterFunc* func = GetFuncByModuleIndex(func_index);
  InterpreterFuncSignature* sig = GetSignatureByEnvIndex(func->sig_index);
  CHECK_RESULT(
      typechecker_on_call(&typechecker, &sig->param_types, &sig->result_types));

  if (func->is_host) {
    CHECK_RESULT(EmitOpcode(InterpreterOpcode::CallHost));
    CHECK_RESULT(EmitI32(TranslateFuncIndexToEnv(func_index)));
  } else {
    CHECK_RESULT(EmitOpcode(InterpreterOpcode::Call));
    CHECK_RESULT(EmitFuncOffset(func->as_defined(), func_index));
  }

  return Result::Ok;
}

Result BinaryReaderInterpreter::OnCallIndirectExpr(uint32_t sig_index) {
  if (module->table_index == WABT_INVALID_INDEX) {
    PrintError("found call_indirect operator, but no table");
    return Result::Error;
  }
  InterpreterFuncSignature* sig = GetSignatureByModuleIndex(sig_index);
  CHECK_RESULT(typechecker_on_call_indirect(&typechecker, &sig->param_types,
                                            &sig->result_types));

  CHECK_RESULT(EmitOpcode(InterpreterOpcode::CallIndirect));
  CHECK_RESULT(EmitI32(module->table_index));
  CHECK_RESULT(EmitI32(TranslateSigIndexToEnv(sig_index)));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnCompareExpr(Opcode opcode) {
  return OnBinaryExpr(opcode);
}

Result BinaryReaderInterpreter::OnConvertExpr(Opcode opcode) {
  return OnUnaryExpr(opcode);
}

Result BinaryReaderInterpreter::OnDropExpr() {
  CHECK_RESULT(typechecker_on_drop(&typechecker));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::Drop));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnI32ConstExpr(uint32_t value) {
  CHECK_RESULT(typechecker_on_const(&typechecker, Type::I32));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::I32Const));
  CHECK_RESULT(EmitI32(value));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnI64ConstExpr(uint64_t value) {
  CHECK_RESULT(typechecker_on_const(&typechecker, Type::I64));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::I64Const));
  CHECK_RESULT(EmitI64(value));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnF32ConstExpr(uint32_t value_bits) {
  CHECK_RESULT(typechecker_on_const(&typechecker, Type::F32));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::F32Const));
  CHECK_RESULT(EmitI32(value_bits));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnF64ConstExpr(uint64_t value_bits) {
  CHECK_RESULT(typechecker_on_const(&typechecker, Type::F64));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::F64Const));
  CHECK_RESULT(EmitI64(value_bits));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnGetGlobalExpr(uint32_t global_index) {
  CHECK_RESULT(CheckGlobal(global_index));
  Type type = GetGlobalTypeByModuleIndex(global_index);
  CHECK_RESULT(typechecker_on_get_global(&typechecker, type));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::GetGlobal));
  CHECK_RESULT(EmitI32(TranslateGlobalIndexToEnv(global_index)));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnSetGlobalExpr(uint32_t global_index) {
  CHECK_RESULT(CheckGlobal(global_index));
  InterpreterGlobal* global = GetGlobalByModuleIndex(global_index);
  if (!global->mutable_) {
    PrintError("can't set_global on immutable global at index %u.",
               global_index);
    return Result::Error;
  }
  CHECK_RESULT(
      typechecker_on_set_global(&typechecker, global->typed_value.type));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::SetGlobal));
  CHECK_RESULT(EmitI32(TranslateGlobalIndexToEnv(global_index)));
  return Result::Ok;
}

uint32_t BinaryReaderInterpreter::TranslateLocalIndex(uint32_t local_index) {
  return typechecker.type_stack.size() +
         current_func->param_and_local_types.size() - local_index;
}

Result BinaryReaderInterpreter::OnGetLocalExpr(uint32_t local_index) {
  CHECK_RESULT(CheckLocal(local_index));
  Type type = GetLocalTypeByIndex(current_func, local_index);
  /* Get the translated index before calling typechecker_on_get_local
   * because it will update the type stack size. We need the index to be
   * relative to the old stack size. */
  uint32_t translated_local_index = TranslateLocalIndex(local_index);
  CHECK_RESULT(typechecker_on_get_local(&typechecker, type));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::GetLocal));
  CHECK_RESULT(EmitI32(translated_local_index));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnSetLocalExpr(uint32_t local_index) {
  CHECK_RESULT(CheckLocal(local_index));
  Type type = GetLocalTypeByIndex(current_func, local_index);
  CHECK_RESULT(typechecker_on_set_local(&typechecker, type));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::SetLocal));
  CHECK_RESULT(EmitI32(TranslateLocalIndex(local_index)));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnTeeLocalExpr(uint32_t local_index) {
  CHECK_RESULT(CheckLocal(local_index));
  Type type = GetLocalTypeByIndex(current_func, local_index);
  CHECK_RESULT(typechecker_on_tee_local(&typechecker, type));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::TeeLocal));
  CHECK_RESULT(EmitI32(TranslateLocalIndex(local_index)));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnGrowMemoryExpr() {
  CHECK_RESULT(CheckHasMemory(Opcode::GrowMemory));
  CHECK_RESULT(typechecker_on_grow_memory(&typechecker));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::GrowMemory));
  CHECK_RESULT(EmitI32(module->memory_index));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnLoadExpr(Opcode opcode,
                                           uint32_t alignment_log2,
                                           uint32_t offset) {
  CHECK_RESULT(CheckHasMemory(opcode));
  CHECK_RESULT(CheckAlign(alignment_log2, get_opcode_memory_size(opcode)));
  CHECK_RESULT(typechecker_on_load(&typechecker, opcode));
  CHECK_RESULT(EmitOpcode(opcode));
  CHECK_RESULT(EmitI32(module->memory_index));
  CHECK_RESULT(EmitI32(offset));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnStoreExpr(Opcode opcode,
                                            uint32_t alignment_log2,
                                            uint32_t offset) {
  CHECK_RESULT(CheckHasMemory(opcode));
  CHECK_RESULT(CheckAlign(alignment_log2, get_opcode_memory_size(opcode)));
  CHECK_RESULT(typechecker_on_store(&typechecker, opcode));
  CHECK_RESULT(EmitOpcode(opcode));
  CHECK_RESULT(EmitI32(module->memory_index));
  CHECK_RESULT(EmitI32(offset));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnCurrentMemoryExpr() {
  CHECK_RESULT(CheckHasMemory(Opcode::CurrentMemory));
  CHECK_RESULT(typechecker_on_current_memory(&typechecker));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::CurrentMemory));
  CHECK_RESULT(EmitI32(module->memory_index));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnNopExpr() {
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnReturnExpr() {
  uint32_t drop_count, keep_count;
  CHECK_RESULT(GetReturnDropKeepCount(&drop_count, &keep_count));
  CHECK_RESULT(typechecker_on_return(&typechecker));
  CHECK_RESULT(EmitDropKeep(drop_count, keep_count));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::Return));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnSelectExpr() {
  CHECK_RESULT(typechecker_on_select(&typechecker));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::Select));
  return Result::Ok;
}

Result BinaryReaderInterpreter::OnUnreachableExpr() {
  CHECK_RESULT(typechecker_on_unreachable(&typechecker));
  CHECK_RESULT(EmitOpcode(InterpreterOpcode::Unreachable));
  return Result::Ok;
}

Result BinaryReaderInterpreter::EndModule() {
  for (ElemSegmentInfo& info : elem_segment_infos) {
    *info.dst = info.func_index;
  }
  for (DataSegmentInfo& info : data_segment_infos) {
    memcpy(info.dst_data, info.src_data, info.size);
  }
  return Result::Ok;
}

}  // namespace

Result read_binary_interpreter(InterpreterEnvironment* env,
                               const void* data,
                               size_t size,
                               const ReadBinaryOptions* options,
                               BinaryErrorHandler* error_handler,
                               DefinedInterpreterModule** out_module) {
  size_t istream_offset = env->istream->data.size();
  DefinedInterpreterModule* module =
      new DefinedInterpreterModule(istream_offset);

  // Need to mark before constructing the reader since it takes ownership of
  // env->istream, which makes env->istream == nullptr.
  InterpreterEnvironmentMark mark = mark_interpreter_environment(env);
  BinaryReaderInterpreter reader(env, module, istream_offset, error_handler);
  env->modules.emplace_back(module);

  Result result = read_binary(data, size, &reader, options);
  env->istream = reader.ReleaseOutputBuffer();
  if (WABT_SUCCEEDED(result)) {
    env->istream->data.resize(reader.get_istream_offset());
    module->istream_end = env->istream->data.size();
    *out_module = module;
  } else {
    reset_interpreter_environment_to_mark(env, mark);
    *out_module = nullptr;
  }
  return result;
}

}  // namespace wabt
