// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/wasm/module-instantiate.h"
#include "src/asmjs/asm-js.h"
#include "src/heap/heap-inl.h"  // For CodeSpaceMemoryModificationScope.
#include "src/property-descriptor.h"
#include "src/utils.h"
#include "src/wasm/js-to-wasm-wrapper-cache-inl.h"
#include "src/wasm/module-compiler.h"
#include "src/wasm/wasm-import-wrapper-cache-inl.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects-inl.h"

#define TRACE(...)                                      \
  do {                                                  \
    if (FLAG_trace_wasm_instances) PrintF(__VA_ARGS__); \
  } while (false)

namespace v8 {
namespace internal {
namespace wasm {

namespace {
byte* raw_buffer_ptr(MaybeHandle<JSArrayBuffer> buffer, int offset) {
  return static_cast<byte*>(buffer.ToHandleChecked()->backing_store()) + offset;
}

uint32_t EvalUint32InitExpr(Handle<WasmInstanceObject> instance,
                            const WasmInitExpr& expr) {
  switch (expr.kind) {
    case WasmInitExpr::kI32Const:
      return expr.val.i32_const;
    case WasmInitExpr::kGlobalIndex: {
      uint32_t offset =
          instance->module()->globals[expr.val.global_index].offset;
      auto raw_addr =
          reinterpret_cast<Address>(
              instance->untagged_globals_buffer()->backing_store()) +
          offset;
      return ReadLittleEndianValue<uint32_t>(raw_addr);
    }
    default:
      UNREACHABLE();
  }
}

// Represents the initialized state of a table.
struct TableInstance {
  Handle<WasmTableObject> table_object;  // WebAssembly.Table instance
  Handle<FixedArray> js_functions;       // JSFunctions exported
  size_t table_size;
};
}  // namespace

// A helper class to simplify instantiating a module from a module object.
// It closes over the {Isolate}, the {ErrorThrower}, etc.
class InstanceBuilder {
 public:
  InstanceBuilder(Isolate* isolate, ErrorThrower* thrower,
                  Handle<WasmModuleObject> module_object,
                  MaybeHandle<JSReceiver> ffi,
                  MaybeHandle<JSArrayBuffer> memory);

  // Build an instance, in all of its glory.
  MaybeHandle<WasmInstanceObject> Build();
  // Run the start function, if any.
  bool ExecuteStartFunction();

 private:
  // A pre-evaluated value to use in import binding.
  struct SanitizedImport {
    Handle<String> module_name;
    Handle<String> import_name;
    Handle<Object> value;
  };

  Isolate* isolate_;
  const WasmFeatures enabled_;
  const WasmModule* const module_;
  ErrorThrower* thrower_;
  Handle<WasmModuleObject> module_object_;
  MaybeHandle<JSReceiver> ffi_;
  MaybeHandle<JSArrayBuffer> memory_;
  Handle<JSArrayBuffer> untagged_globals_;
  Handle<FixedArray> tagged_globals_;
  std::vector<TableInstance> table_instances_;
  std::vector<Handle<WasmExceptionObject>> exception_wrappers_;
  Handle<WasmExportedFunction> start_function_;
  JSToWasmWrapperCache js_to_wasm_cache_;
  std::vector<SanitizedImport> sanitized_imports_;

  UseTrapHandler use_trap_handler() const {
    return module_object_->native_module()->use_trap_handler() ? kUseTrapHandler
                                                               : kNoTrapHandler;
  }

// Helper routines to print out errors with imports.
#define ERROR_THROWER_WITH_MESSAGE(TYPE)                                      \
  void Report##TYPE(const char* error, uint32_t index,                        \
                    Handle<String> module_name, Handle<String> import_name) { \
    thrower_->TYPE("Import #%d module=\"%s\" function=\"%s\" error: %s",      \
                   index, module_name->ToCString().get(),                     \
                   import_name->ToCString().get(), error);                    \
  }                                                                           \
                                                                              \
  MaybeHandle<Object> Report##TYPE(const char* error, uint32_t index,         \
                                   Handle<String> module_name) {              \
    thrower_->TYPE("Import #%d module=\"%s\" error: %s", index,               \
                   module_name->ToCString().get(), error);                    \
    return MaybeHandle<Object>();                                             \
  }

  ERROR_THROWER_WITH_MESSAGE(LinkError)
  ERROR_THROWER_WITH_MESSAGE(TypeError)

#undef ERROR_THROWER_WITH_MESSAGE

  // Look up an import value in the {ffi_} object.
  MaybeHandle<Object> LookupImport(uint32_t index, Handle<String> module_name,
                                   Handle<String> import_name);

  // Look up an import value in the {ffi_} object specifically for linking an
  // asm.js module. This only performs non-observable lookups, which allows
  // falling back to JavaScript proper (and hence re-executing all lookups) if
  // module instantiation fails.
  MaybeHandle<Object> LookupImportAsm(uint32_t index,
                                      Handle<String> import_name);

  // Load data segments into the memory.
  void LoadDataSegments(Handle<WasmInstanceObject> instance);

  void WriteGlobalValue(const WasmGlobal& global, double value);
  void WriteGlobalValue(const WasmGlobal& global,
                        Handle<WasmGlobalObject> value);

  void WriteGlobalAnyRef(const WasmGlobal& global, Handle<Object> value);

  void SanitizeImports();

  // Find the imported memory buffer if there is one. This is used to see if we
  // need to recompile with bounds checks before creating the instance.
  MaybeHandle<JSArrayBuffer> FindImportedMemoryBuffer() const;

  // Processes a single imported function.
  bool ProcessImportedFunction(Handle<WasmInstanceObject> instance,
                               int import_index, int func_index,
                               Handle<String> module_name,
                               Handle<String> import_name,
                               Handle<Object> value);

  // Process a single imported table.
  bool ProcessImportedTable(Handle<WasmInstanceObject> instance,
                            int import_index, int table_index,
                            Handle<String> module_name,
                            Handle<String> import_name, Handle<Object> value);

  // Process a single imported memory.
  bool ProcessImportedMemory(Handle<WasmInstanceObject> instance,
                             int import_index, Handle<String> module_name,
                             Handle<String> import_name, Handle<Object> value);

  // Process a single imported global.
  bool ProcessImportedGlobal(Handle<WasmInstanceObject> instance,
                             int import_index, int global_index,
                             Handle<String> module_name,
                             Handle<String> import_name, Handle<Object> value);

  // Process a single imported WasmGlobalObject.
  bool ProcessImportedWasmGlobalObject(Handle<WasmInstanceObject> instance,
                                       int import_index,
                                       Handle<String> module_name,
                                       Handle<String> import_name,
                                       const WasmGlobal& global,
                                       Handle<WasmGlobalObject> global_object);

  // Process the imports, including functions, tables, globals, and memory, in
  // order, loading them from the {ffi_} object. Returns the number of imported
  // functions.
  int ProcessImports(Handle<WasmInstanceObject> instance);

  template <typename T>
  T* GetRawGlobalPtr(const WasmGlobal& global);

  // Process initialization of globals.
  void InitGlobals();

  // Allocate memory for a module instance as a new JSArrayBuffer.
  Handle<JSArrayBuffer> AllocateMemory(uint32_t initial_pages,
                                       uint32_t maximum_pages);

  bool NeedsWrappers() const;

  // Process the exports, creating wrappers for functions, tables, memories,
  // and globals.
  void ProcessExports(Handle<WasmInstanceObject> instance);

  void InitializeTables(Handle<WasmInstanceObject> instance);

  void LoadTableSegments(Handle<WasmInstanceObject> instance);

  // Creates new exception tags for all exceptions. Note that some tags might
  // already exist if they were imported, those tags will be re-used.
  void InitializeExceptions(Handle<WasmInstanceObject> instance);
};

MaybeHandle<WasmInstanceObject> InstantiateToInstanceObject(
    Isolate* isolate, ErrorThrower* thrower,
    Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
    MaybeHandle<JSArrayBuffer> memory) {
  InstanceBuilder builder(isolate, thrower, module_object, imports, memory);
  auto instance = builder.Build();
  if (!instance.is_null() && builder.ExecuteStartFunction()) {
    return instance;
  }
  DCHECK(isolate->has_pending_exception() || thrower->error());
  return {};
}

InstanceBuilder::InstanceBuilder(Isolate* isolate, ErrorThrower* thrower,
                                 Handle<WasmModuleObject> module_object,
                                 MaybeHandle<JSReceiver> ffi,
                                 MaybeHandle<JSArrayBuffer> memory)
    : isolate_(isolate),
      enabled_(module_object->native_module()->enabled_features()),
      module_(module_object->module()),
      thrower_(thrower),
      module_object_(module_object),
      ffi_(ffi),
      memory_(memory) {
  sanitized_imports_.reserve(module_->import_table.size());
}

// Build an instance, in all of its glory.
MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "InstanceBuilder::Build");
  // Check that an imports argument was provided, if the module requires it.
  // No point in continuing otherwise.
  if (!module_->import_table.empty() && ffi_.is_null()) {
    thrower_->TypeError(
        "Imports argument must be present and must be an object");
    return {};
  }

  SanitizeImports();
  if (thrower_->error()) return {};

  // TODO(6792): No longer needed once WebAssembly code is off heap.
  CodeSpaceMemoryModificationScope modification_scope(isolate_->heap());
  // From here on, we expect the build pipeline to run without exiting to JS.
  DisallowJavascriptExecution no_js(isolate_);
  // Record build time into correct bucket, then build instance.
  TimedHistogramScope wasm_instantiate_module_time_scope(SELECT_WASM_COUNTER(
      isolate_->counters(), module_->origin, wasm_instantiate, module_time));

  //--------------------------------------------------------------------------
  // Allocate the memory array buffer.
  //--------------------------------------------------------------------------
  // We allocate the memory buffer before cloning or reusing the compiled module
  // so we will know whether we need to recompile with bounds checks.
  uint32_t initial_pages = module_->initial_pages;
  auto initial_pages_counter = SELECT_WASM_COUNTER(
      isolate_->counters(), module_->origin, wasm, min_mem_pages_count);
  initial_pages_counter->AddSample(initial_pages);
  // Asm.js has memory_ already set at this point, so we don't want to
  // overwrite it.
  if (memory_.is_null()) {
    memory_ = FindImportedMemoryBuffer();
  }
  if (!memory_.is_null()) {
    // Set externally passed ArrayBuffer non detachable.
    Handle<JSArrayBuffer> memory = memory_.ToHandleChecked();
    memory->set_is_detachable(false);

    DCHECK_IMPLIES(use_trap_handler(), module_->origin == kAsmJsOrigin ||
                                           memory->is_wasm_memory() ||
                                           memory->backing_store() == nullptr);
  } else if (initial_pages > 0 || use_trap_handler()) {
    // We need to unconditionally create a guard region if using trap handlers,
    // even when the size is zero to prevent null-dereference issues
    // (e.g. https://crbug.com/769637).
    // Allocate memory if the initial size is more than 0 pages.
    memory_ = AllocateMemory(initial_pages, module_->maximum_pages);
    if (memory_.is_null()) {
      // failed to allocate memory
      DCHECK(isolate_->has_pending_exception() || thrower_->error());
      return {};
    }
  }

  //--------------------------------------------------------------------------
  // Recompile module if using trap handlers but could not get guarded memory
  //--------------------------------------------------------------------------
  if (module_->origin == kWasmOrigin && use_trap_handler()) {
    // Make sure the memory has suitable guard regions.
    WasmMemoryTracker* const memory_tracker =
        isolate_->wasm_engine()->memory_tracker();

    if (!memory_tracker->HasFullGuardRegions(
            memory_.ToHandleChecked()->backing_store())) {
      if (!FLAG_wasm_trap_handler_fallback) {
        thrower_->LinkError(
            "Provided memory is lacking guard regions but fallback was "
            "disabled.");
        return {};
      }

      TRACE("Recompiling module without bounds checks\n");
      ErrorThrower thrower(isolate_, "recompile");
      auto native_module = module_object_->native_module();
      CompileNativeModuleWithExplicitBoundsChecks(isolate_, &thrower, module_,
                                                  native_module);
      if (thrower.error()) {
        return {};
      }
      DCHECK(!native_module->use_trap_handler());
    }
  }

  //--------------------------------------------------------------------------
  // Create the WebAssembly.Instance object.
  //--------------------------------------------------------------------------
  NativeModule* native_module = module_object_->native_module();
  TRACE("New module instantiation for %p\n", native_module);
  Handle<WasmInstanceObject> instance =
      WasmInstanceObject::New(isolate_, module_object_);
  NativeModuleModificationScope native_modification_scope(native_module);

  //--------------------------------------------------------------------------
  // Set up the globals for the new instance.
  //--------------------------------------------------------------------------
  uint32_t untagged_globals_buffer_size = module_->untagged_globals_buffer_size;
  if (untagged_globals_buffer_size > 0) {
    void* backing_store = isolate_->array_buffer_allocator()->Allocate(
        untagged_globals_buffer_size);
    if (backing_store == nullptr) {
      thrower_->RangeError("Out of memory: wasm globals");
      return {};
    }
    untagged_globals_ =
        isolate_->factory()->NewJSArrayBuffer(SharedFlag::kNotShared, TENURED);
    constexpr bool is_external = false;
    constexpr bool is_wasm_memory = false;
    JSArrayBuffer::Setup(untagged_globals_, isolate_, is_external,
                         backing_store, untagged_globals_buffer_size,
                         SharedFlag::kNotShared, is_wasm_memory);
    if (untagged_globals_.is_null()) {
      thrower_->RangeError("Out of memory: wasm globals");
      return {};
    }
    instance->set_globals_start(
        reinterpret_cast<byte*>(untagged_globals_->backing_store()));
    instance->set_untagged_globals_buffer(*untagged_globals_);
  }

  uint32_t tagged_globals_buffer_size = module_->tagged_globals_buffer_size;
  if (tagged_globals_buffer_size > 0) {
    tagged_globals_ = isolate_->factory()->NewFixedArray(
        static_cast<int>(tagged_globals_buffer_size));
    instance->set_tagged_globals_buffer(*tagged_globals_);
  }

  //--------------------------------------------------------------------------
  // Set up the array of references to imported globals' array buffers.
  //--------------------------------------------------------------------------
  if (module_->num_imported_mutable_globals > 0) {
    // TODO(binji): This allocates one slot for each mutable global, which is
    // more than required if multiple globals are imported from the same
    // module.
    Handle<FixedArray> buffers_array = isolate_->factory()->NewFixedArray(
        module_->num_imported_mutable_globals, TENURED);
    instance->set_imported_mutable_globals_buffers(*buffers_array);
  }

  //--------------------------------------------------------------------------
  // Set up the exception table used for exception tag checks.
  //--------------------------------------------------------------------------
  int exceptions_count = static_cast<int>(module_->exceptions.size());
  if (exceptions_count > 0) {
    Handle<FixedArray> exception_table =
        isolate_->factory()->NewFixedArray(exceptions_count, TENURED);
    instance->set_exceptions_table(*exception_table);
    exception_wrappers_.resize(exceptions_count);
  }

  //--------------------------------------------------------------------------
  // Set up table storage space.
  //--------------------------------------------------------------------------
  int table_count = static_cast<int>(module_->tables.size());
  Handle<FixedArray> tables = isolate_->factory()->NewFixedArray(table_count);
  for (int i = module_->num_imported_tables; i < table_count; i++) {
    const WasmTable& table = module_->tables[i];
    Handle<WasmTableObject> table_obj = WasmTableObject::New(
        isolate_, table.initial_size, table.maximum_size, nullptr);
    tables->set(i, *table_obj);
  }
  instance->set_tables(*tables);

  table_instances_.resize(table_count);

  //--------------------------------------------------------------------------
  // Process the imports for the module.
  //--------------------------------------------------------------------------
  int num_imported_functions = ProcessImports(instance);
  if (num_imported_functions < 0) return {};

  //--------------------------------------------------------------------------
  // Process the initialization for the module's globals.
  //--------------------------------------------------------------------------
  InitGlobals();

  //--------------------------------------------------------------------------
  // Initialize the indirect tables.
  //--------------------------------------------------------------------------
  if (table_count > 0) {
    InitializeTables(instance);
  }

  //--------------------------------------------------------------------------
  // Initialize the exceptions table.
  //--------------------------------------------------------------------------
  if (exceptions_count > 0) {
    InitializeExceptions(instance);
  }

  //--------------------------------------------------------------------------
  // Create the WebAssembly.Memory object.
  //--------------------------------------------------------------------------
  if (module_->has_memory) {
    if (!instance->has_memory_object()) {
      // No memory object exists. Create one.
      Handle<WasmMemoryObject> memory_object = WasmMemoryObject::New(
          isolate_, memory_,
          module_->maximum_pages != 0 ? module_->maximum_pages : -1);
      instance->set_memory_object(*memory_object);
    }

    // Add the instance object to the list of instances for this memory.
    Handle<WasmMemoryObject> memory_object(instance->memory_object(), isolate_);
    WasmMemoryObject::AddInstance(isolate_, memory_object, instance);

    if (!memory_.is_null()) {
      // Double-check the {memory} array buffer matches the instance.
      Handle<JSArrayBuffer> memory = memory_.ToHandleChecked();
      CHECK_EQ(instance->memory_size(), memory->byte_length());
      CHECK_EQ(instance->memory_start(), memory->backing_store());
    }
  }

  //--------------------------------------------------------------------------
  // Check that indirect function table segments are within bounds.
  //--------------------------------------------------------------------------
  for (const WasmElemSegment& elem_segment : module_->elem_segments) {
    if (!elem_segment.active) continue;
    DCHECK(elem_segment.table_index < table_instances_.size());
    uint32_t base = EvalUint32InitExpr(instance, elem_segment.offset);
    size_t table_size = table_instances_[elem_segment.table_index].table_size;
    if (!IsInBounds(base, elem_segment.entries.size(), table_size)) {
      thrower_->LinkError("table initializer is out of bounds");
      return {};
    }
  }

  //--------------------------------------------------------------------------
  // Check that memory segments are within bounds.
  //--------------------------------------------------------------------------
  for (const WasmDataSegment& seg : module_->data_segments) {
    if (!seg.active) continue;
    uint32_t base = EvalUint32InitExpr(instance, seg.dest_addr);
    if (!IsInBounds(base, seg.source.length(), instance->memory_size())) {
      thrower_->LinkError("data segment is out of bounds");
      return {};
    }
  }

  //--------------------------------------------------------------------------
  // Set up the exports object for the new instance.
  //--------------------------------------------------------------------------
  ProcessExports(instance);
  if (thrower_->error()) return {};

  //--------------------------------------------------------------------------
  // Initialize the indirect function tables.
  //--------------------------------------------------------------------------
  if (table_count > 0) {
    LoadTableSegments(instance);
  }

  //--------------------------------------------------------------------------
  // Initialize the memory by loading data segments.
  //--------------------------------------------------------------------------
  if (module_->data_segments.size() > 0) {
    LoadDataSegments(instance);
  }

  //--------------------------------------------------------------------------
  // Debugging support.
  //--------------------------------------------------------------------------
  // Set all breakpoints that were set on the shared module.
  WasmModuleObject::SetBreakpointsOnNewInstance(module_object_, instance);

  if (FLAG_wasm_interpret_all && module_->origin == kWasmOrigin) {
    Handle<WasmDebugInfo> debug_info =
        WasmInstanceObject::GetOrCreateDebugInfo(instance);
    std::vector<int> func_indexes;
    for (int func_index = num_imported_functions,
             num_wasm_functions = static_cast<int>(module_->functions.size());
         func_index < num_wasm_functions; ++func_index) {
      func_indexes.push_back(func_index);
    }
    WasmDebugInfo::RedirectToInterpreter(debug_info, VectorOf(func_indexes));
  }

  //--------------------------------------------------------------------------
  // Create a wrapper for the start function.
  //--------------------------------------------------------------------------
  if (module_->start_function_index >= 0) {
    int start_index = module_->start_function_index;
    auto& function = module_->functions[start_index];
    Handle<Code> wrapper_code = js_to_wasm_cache_.GetOrCompileJSToWasmWrapper(
        isolate_, function.sig, function.imported);
    // TODO(clemensh): Don't generate an exported function for the start
    // function. Use CWasmEntry instead.
    start_function_ = WasmExportedFunction::New(
        isolate_, instance, MaybeHandle<String>(), start_index,
        static_cast<int>(function.sig->parameter_count()), wrapper_code);
  }

  DCHECK(!isolate_->has_pending_exception());
  TRACE("Successfully built instance for module %p\n",
        module_object_->native_module());
  return instance;
}

bool InstanceBuilder::ExecuteStartFunction() {
  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
               "InstanceBuilder::ExecuteStartFunction");
  if (start_function_.is_null()) return true;  // No start function.

  HandleScope scope(isolate_);
  // Call the JS function.
  Handle<Object> undefined = isolate_->factory()->undefined_value();
  MaybeHandle<Object> retval =
      Execution::Call(isolate_, start_function_, undefined, 0, nullptr);

  if (retval.is_null()) {
    DCHECK(isolate_->has_pending_exception());
    return false;
  }
  return true;
}

// Look up an import value in the {ffi_} object.
MaybeHandle<Object> InstanceBuilder::LookupImport(uint32_t index,
                                                  Handle<String> module_name,

                                                  Handle<String> import_name) {
  // We pre-validated in the js-api layer that the ffi object is present, and
  // a JSObject, if the module has imports.
  DCHECK(!ffi_.is_null());

  // Look up the module first.
  MaybeHandle<Object> result = Object::GetPropertyOrElement(
      isolate_, ffi_.ToHandleChecked(), module_name);
  if (result.is_null()) {
    return ReportTypeError("module not found", index, module_name);
  }

  Handle<Object> module = result.ToHandleChecked();

  // Look up the value in the module.
  if (!module->IsJSReceiver()) {
    return ReportTypeError("module is not an object or function", index,
                           module_name);
  }

  result = Object::GetPropertyOrElement(isolate_, module, import_name);
  if (result.is_null()) {
    ReportLinkError("import not found", index, module_name, import_name);
    return MaybeHandle<JSFunction>();
  }

  return result;
}

// Look up an import value in the {ffi_} object specifically for linking an
// asm.js module. This only performs non-observable lookups, which allows
// falling back to JavaScript proper (and hence re-executing all lookups) if
// module instantiation fails.
MaybeHandle<Object> InstanceBuilder::LookupImportAsm(
    uint32_t index, Handle<String> import_name) {
  // Check that a foreign function interface object was provided.
  if (ffi_.is_null()) {
    return ReportLinkError("missing imports object", index, import_name);
  }

  // Perform lookup of the given {import_name} without causing any observable
  // side-effect. We only accept accesses that resolve to data properties,
  // which is indicated by the asm.js spec in section 7 ("Linking") as well.
  Handle<Object> result;
  LookupIterator it = LookupIterator::PropertyOrElement(
      isolate_, ffi_.ToHandleChecked(), import_name);
  switch (it.state()) {
    case LookupIterator::ACCESS_CHECK:
    case LookupIterator::INTEGER_INDEXED_EXOTIC:
    case LookupIterator::INTERCEPTOR:
    case LookupIterator::JSPROXY:
    case LookupIterator::ACCESSOR:
    case LookupIterator::TRANSITION:
      return ReportLinkError("not a data property", index, import_name);
    case LookupIterator::NOT_FOUND:
      // Accepting missing properties as undefined does not cause any
      // observable difference from JavaScript semantics, we are lenient.
      result = isolate_->factory()->undefined_value();
      break;
    case LookupIterator::DATA:
      result = it.GetDataValue();
      break;
  }

  return result;
}

// Load data segments into the memory.
void InstanceBuilder::LoadDataSegments(Handle<WasmInstanceObject> instance) {
  Vector<const uint8_t> wire_bytes =
      module_object_->native_module()->wire_bytes();
  for (const WasmDataSegment& segment : module_->data_segments) {
    uint32_t source_size = segment.source.length();
    // Segments of size == 0 are just nops.
    if (source_size == 0) continue;
    // Passive segments are not copied during instantiation.
    if (!segment.active) continue;
    uint32_t dest_offset = EvalUint32InitExpr(instance, segment.dest_addr);
    DCHECK(IsInBounds(dest_offset, source_size, instance->memory_size()));
    byte* dest = instance->memory_start() + dest_offset;
    const byte* src = wire_bytes.start() + segment.source.offset();
    memcpy(dest, src, source_size);
  }
}

void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global, double num) {
  TRACE("init [globals_start=%p + %u] = %lf, type = %s\n",
        reinterpret_cast<void*>(raw_buffer_ptr(untagged_globals_, 0)),
        global.offset, num, ValueTypes::TypeName(global.type));
  switch (global.type) {
    case kWasmI32:
      WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global),
                                      static_cast<int32_t>(num));
      break;
    case kWasmI64:
      WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global),
                                      static_cast<int64_t>(num));
      break;
    case kWasmF32:
      WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global),
                                    static_cast<float>(num));
      break;
    case kWasmF64:
      WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global),
                                     static_cast<double>(num));
      break;
    default:
      UNREACHABLE();
  }
}

void InstanceBuilder::WriteGlobalValue(const WasmGlobal& global,
                                       Handle<WasmGlobalObject> value) {
  TRACE("init [globals_start=%p + %u] = ",
        reinterpret_cast<void*>(raw_buffer_ptr(untagged_globals_, 0)),
        global.offset);
  switch (global.type) {
    case kWasmI32: {
      int32_t num = value->GetI32();
      WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global), num);
      TRACE("%d", num);
      break;
    }
    case kWasmI64: {
      int64_t num = value->GetI64();
      WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global), num);
      TRACE("%" PRId64, num);
      break;
    }
    case kWasmF32: {
      float num = value->GetF32();
      WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global), num);
      TRACE("%f", num);
      break;
    }
    case kWasmF64: {
      double num = value->GetF64();
      WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global), num);
      TRACE("%lf", num);
      break;
    }
    default:
      UNREACHABLE();
  }
  TRACE(", type = %s (from WebAssembly.Global)\n",
        ValueTypes::TypeName(global.type));
}

void InstanceBuilder::WriteGlobalAnyRef(const WasmGlobal& global,
                                        Handle<Object> value) {
  tagged_globals_->set(global.offset, *value, UPDATE_WRITE_BARRIER);
}

void InstanceBuilder::SanitizeImports() {
  Vector<const uint8_t> wire_bytes =
      module_object_->native_module()->wire_bytes();
  for (size_t index = 0; index < module_->import_table.size(); ++index) {
    const WasmImport& import = module_->import_table[index];

    Handle<String> module_name;
    MaybeHandle<String> maybe_module_name =
        WasmModuleObject::ExtractUtf8StringFromModuleBytes(isolate_, wire_bytes,
                                                           import.module_name);
    if (!maybe_module_name.ToHandle(&module_name)) {
      thrower_->LinkError("Could not resolve module name for import %zu",
                          index);
      return;
    }

    Handle<String> import_name;
    MaybeHandle<String> maybe_import_name =
        WasmModuleObject::ExtractUtf8StringFromModuleBytes(isolate_, wire_bytes,
                                                           import.field_name);
    if (!maybe_import_name.ToHandle(&import_name)) {
      thrower_->LinkError("Could not resolve import name for import %zu",
                          index);
      return;
    }

    int int_index = static_cast<int>(index);
    MaybeHandle<Object> result =
        module_->origin == kAsmJsOrigin
            ? LookupImportAsm(int_index, import_name)
            : LookupImport(int_index, module_name, import_name);
    if (thrower_->error()) {
      thrower_->LinkError("Could not find value for import %zu", index);
      return;
    }
    Handle<Object> value = result.ToHandleChecked();
    sanitized_imports_.push_back({module_name, import_name, value});
  }
}

MaybeHandle<JSArrayBuffer> InstanceBuilder::FindImportedMemoryBuffer() const {
  DCHECK_EQ(module_->import_table.size(), sanitized_imports_.size());
  for (size_t index = 0; index < module_->import_table.size(); index++) {
    const WasmImport& import = module_->import_table[index];

    if (import.kind == kExternalMemory) {
      const auto& value = sanitized_imports_[index].value;
      if (!value->IsWasmMemoryObject()) {
        return {};
      }
      auto memory = Handle<WasmMemoryObject>::cast(value);
      Handle<JSArrayBuffer> buffer(memory->array_buffer(), isolate_);
      return buffer;
    }
  }
  return {};
}

bool InstanceBuilder::ProcessImportedFunction(
    Handle<WasmInstanceObject> instance, int import_index, int func_index,
    Handle<String> module_name, Handle<String> import_name,
    Handle<Object> value) {
  // Function imports must be callable.
  if (!value->IsCallable()) {
    ReportLinkError("function import requires a callable", import_index,
                    module_name, import_name);
    return false;
  }
  auto js_receiver = Handle<JSReceiver>::cast(value);
  FunctionSig* expected_sig = module_->functions[func_index].sig;
  auto kind = compiler::GetWasmImportCallKind(js_receiver, expected_sig,
                                              enabled_.bigint);
  switch (kind) {
    case compiler::WasmImportCallKind::kLinkError:
      ReportLinkError("imported function does not match the expected type",
                      import_index, module_name, import_name);
      return false;
    case compiler::WasmImportCallKind::kWasmToWasm: {
      // The imported function is a WASM function from another instance.
      auto imported_function = Handle<WasmExportedFunction>::cast(value);
      Handle<WasmInstanceObject> imported_instance(
          imported_function->instance(), isolate_);
      // The import reference is the instance object itself.
      Address imported_target = imported_function->GetWasmCallTarget();
      ImportedFunctionEntry entry(instance, func_index);
      entry.SetWasmToWasm(*imported_instance, imported_target);
      break;
    }
    default: {
      // The imported function is a callable.
      NativeModule* native_module = instance->module_object()->native_module();
      WasmCode* wasm_code = native_module->import_wrapper_cache()->GetOrCompile(
          isolate_->wasm_engine(), isolate_->counters(), kind, expected_sig);
      ImportedFunctionEntry entry(instance, func_index);
      if (wasm_code->kind() == WasmCode::kWasmToJsWrapper) {
        // Wasm to JS wrappers are treated specially in the import table.
        entry.SetWasmToJs(isolate_, js_receiver, wasm_code);
      } else {
        // Wasm math intrinsics are compiled as regular Wasm functions.
        DCHECK(kind >= compiler::WasmImportCallKind::kFirstMathIntrinsic &&
               kind <= compiler::WasmImportCallKind::kLastMathIntrinsic);
        entry.SetWasmToWasm(*instance, wasm_code->instruction_start());
      }
      break;
    }
  }
  return true;
}

bool InstanceBuilder::ProcessImportedTable(Handle<WasmInstanceObject> instance,
                                           int import_index, int table_index,
                                           Handle<String> module_name,
                                           Handle<String> import_name,
                                           Handle<Object> value) {
  if (!value->IsWasmTableObject()) {
    ReportLinkError("table import requires a WebAssembly.Table", import_index,
                    module_name, import_name);
    return false;
  }
  const WasmTable& table = module_->tables[table_index];
  TableInstance& table_instance = table_instances_[table_index];
  table_instance.table_object = Handle<WasmTableObject>::cast(value);
  instance->set_table_object(*table_instance.table_object);
  table_instance.js_functions =
      Handle<FixedArray>(table_instance.table_object->elements(), isolate_);

  int imported_table_size = table_instance.js_functions->length();
  if (imported_table_size < static_cast<int>(table.initial_size)) {
    thrower_->LinkError("table import %d is smaller than initial %d, got %u",
                        import_index, table.initial_size, imported_table_size);
    return false;
  }

  if (table.has_maximum_size) {
    int64_t imported_maximum_size =
        table_instance.table_object->maximum_length()->Number();
    if (imported_maximum_size < 0) {
      thrower_->LinkError("table import %d has no maximum length, expected %d",
                          import_index, table.maximum_size);
      return false;
    }
    if (imported_maximum_size > table.maximum_size) {
      thrower_->LinkError("table import %d has a larger maximum size %" PRIx64
                          " than the module's declared maximum %u",
                          import_index, imported_maximum_size,
                          table.maximum_size);
      return false;
    }
  }

  // Allocate a new dispatch table.
  if (!instance->has_indirect_function_table()) {
    WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
        instance, imported_table_size);
    table_instances_[table_index].table_size = imported_table_size;
  }
  // Initialize the dispatch table with the (foreign) JS functions
  // that are already in the table.
  for (int i = 0; i < imported_table_size; ++i) {
    Handle<Object> val(table_instance.js_functions->get(i), isolate_);
    // TODO(mtrofin): this is the same logic as WasmTableObject::Set:
    // insert in the local table a wrapper from the other module, and add
    // a reference to the owning instance of the other module.
    if (!val->IsJSFunction()) continue;
    if (!WasmExportedFunction::IsWasmExportedFunction(*val)) {
      thrower_->LinkError("table import %d[%d] is not a wasm function",
                          import_index, i);
      return false;
    }
    auto target_func = Handle<WasmExportedFunction>::cast(val);
    Handle<WasmInstanceObject> target_instance =
        handle(target_func->instance(), isolate_);
    // Look up the signature's canonical id. If there is no canonical
    // id, then the signature does not appear at all in this module,
    // so putting {-1} in the table will cause checks to always fail.
    FunctionSig* sig = target_func->sig();
    IndirectFunctionTableEntry(instance, i)
        .Set(module_->signature_map.Find(*sig), target_instance,
             target_func->function_index());
  }
  return true;
}

bool InstanceBuilder::ProcessImportedMemory(Handle<WasmInstanceObject> instance,
                                            int import_index,
                                            Handle<String> module_name,
                                            Handle<String> import_name,
                                            Handle<Object> value) {
  // Validation should have failed if more than one memory object was
  // provided.
  DCHECK(!instance->has_memory_object());
  if (!value->IsWasmMemoryObject()) {
    ReportLinkError("memory import must be a WebAssembly.Memory object",
                    import_index, module_name, import_name);
    return false;
  }
  auto memory = Handle<WasmMemoryObject>::cast(value);
  instance->set_memory_object(*memory);
  Handle<JSArrayBuffer> buffer(memory->array_buffer(), isolate_);
  // memory_ should have already been assigned in Build().
  DCHECK_EQ(*memory_.ToHandleChecked(), *buffer);
  uint32_t imported_cur_pages =
      static_cast<uint32_t>(buffer->byte_length() / kWasmPageSize);
  if (imported_cur_pages < module_->initial_pages) {
    thrower_->LinkError("memory import %d is smaller than initial %u, got %u",
                        import_index, module_->initial_pages,
                        imported_cur_pages);
    return false;
  }
  int32_t imported_maximum_pages = memory->maximum_pages();
  if (module_->has_maximum_pages) {
    if (imported_maximum_pages < 0) {
      thrower_->LinkError(
          "memory import %d has no maximum limit, expected at most %u",
          import_index, imported_maximum_pages);
      return false;
    }
    if (static_cast<uint32_t>(imported_maximum_pages) >
        module_->maximum_pages) {
      thrower_->LinkError(
          "memory import %d has a larger maximum size %u than the "
          "module's declared maximum %u",
          import_index, imported_maximum_pages, module_->maximum_pages);
      return false;
    }
  }
  if (module_->has_shared_memory != buffer->is_shared()) {
    thrower_->LinkError(
        "mismatch in shared state of memory, declared = %d, imported = %d",
        module_->has_shared_memory, buffer->is_shared());
    return false;
  }

  return true;
}

bool InstanceBuilder::ProcessImportedWasmGlobalObject(
    Handle<WasmInstanceObject> instance, int import_index,
    Handle<String> module_name, Handle<String> import_name,
    const WasmGlobal& global, Handle<WasmGlobalObject> global_object) {
  if (global_object->type() != global.type) {
    ReportLinkError("imported global does not match the expected type",
                    import_index, module_name, import_name);
    return false;
  }
  if (global_object->is_mutable() != global.mutability) {
    ReportLinkError("imported global does not match the expected mutability",
                    import_index, module_name, import_name);
    return false;
  }
  if (global.mutability) {
    DCHECK_LT(global.index, module_->num_imported_mutable_globals);
    Handle<Object> buffer;
    Address address_or_offset;
    if (global.type == kWasmAnyRef) {
      static_assert(sizeof(global_object->offset()) <= sizeof(Address),
                    "The offset into the globals buffer does not fit into "
                    "the imported_mutable_globals array");
      buffer = handle(global_object->tagged_buffer(), isolate_);
      // For anyref globals we use a relative offset, not an absolute address.
      address_or_offset = static_cast<Address>(global_object->offset());
    } else {
      buffer = handle(global_object->untagged_buffer(), isolate_);
      // It is safe in this case to store the raw pointer to the buffer
      // since the backing store of the JSArrayBuffer will not be
      // relocated.
      address_or_offset = reinterpret_cast<Address>(raw_buffer_ptr(
          Handle<JSArrayBuffer>::cast(buffer), global_object->offset()));
    }
    instance->imported_mutable_globals_buffers()->set(global.index, *buffer);
    instance->imported_mutable_globals()[global.index] = address_or_offset;
    return true;
  }

  WriteGlobalValue(global, global_object);
  return true;
}

bool InstanceBuilder::ProcessImportedGlobal(Handle<WasmInstanceObject> instance,
                                            int import_index, int global_index,
                                            Handle<String> module_name,
                                            Handle<String> import_name,
                                            Handle<Object> value) {
  // Immutable global imports are converted to numbers and written into
  // the {untagged_globals_} array buffer.
  //
  // Mutable global imports instead have their backing array buffers
  // referenced by this instance, and store the address of the imported
  // global in the {imported_mutable_globals_} array.
  const WasmGlobal& global = module_->globals[global_index];

  // The mutable-global proposal allows importing i64 values, but only if
  // they are passed as a WebAssembly.Global object.
  //
  // However, the bigint proposal allows importing constant i64 values,
  // as non WebAssembly.Global object.
  if (global.type == kWasmI64 && !enabled_.bigint &&
      !value->IsWasmGlobalObject()) {
    ReportLinkError("global import cannot have type i64", import_index,
                    module_name, import_name);
    return false;
  }
  if (module_->origin == kAsmJsOrigin) {
    // Accepting {JSFunction} on top of just primitive values here is a
    // workaround to support legacy asm.js code with broken binding. Note
    // that using {NaN} (or Smi::kZero) here is what using the observable
    // conversion via {ToPrimitive} would produce as well.
    // TODO(mstarzinger): Still observable if Function.prototype.valueOf
    // or friends are patched, we might need to check for that as well.
    if (value->IsJSFunction()) value = isolate_->factory()->nan_value();
    if (value->IsPrimitive() && !value->IsSymbol()) {
      if (global.type == kWasmI32) {
        value = Object::ToInt32(isolate_, value).ToHandleChecked();
      } else {
        value = Object::ToNumber(isolate_, value).ToHandleChecked();
      }
    }
  }

  if (value->IsWasmGlobalObject()) {
    auto global_object = Handle<WasmGlobalObject>::cast(value);
    return ProcessImportedWasmGlobalObject(instance, import_index, module_name,
                                           import_name, global, global_object);
  }

  if (global.mutability) {
    ReportLinkError(
        "imported mutable global must be a WebAssembly.Global object",
        import_index, module_name, import_name);
    return false;
  }

  if (global.type == ValueType::kWasmAnyRef) {
    WriteGlobalAnyRef(global, value);
    return true;
  }

  if (value->IsNumber()) {
    WriteGlobalValue(global, value->Number());
    return true;
  }

  if (enabled_.bigint && global.type == kWasmI64) {
    Handle<BigInt> bigint;

    if (!BigInt::FromObject(isolate_, value).ToHandle(&bigint)) {
      return false;
    }
    WriteGlobalValue(global, bigint->AsInt64());
    return true;
  }

  ReportLinkError("global import must be a number or WebAssembly.Global object",
                  import_index, module_name, import_name);
  return false;
}

// Process the imports, including functions, tables, globals, and memory, in
// order, loading them from the {ffi_} object. Returns the number of imported
// functions.
int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
  int num_imported_functions = 0;
  int num_imported_tables = 0;

  DCHECK_EQ(module_->import_table.size(), sanitized_imports_.size());
  int num_imports = static_cast<int>(module_->import_table.size());
  for (int index = 0; index < num_imports; ++index) {
    const WasmImport& import = module_->import_table[index];

    Handle<String> module_name = sanitized_imports_[index].module_name;
    Handle<String> import_name = sanitized_imports_[index].import_name;
    Handle<Object> value = sanitized_imports_[index].value;

    switch (import.kind) {
      case kExternalFunction: {
        uint32_t func_index = import.index;
        DCHECK_EQ(num_imported_functions, func_index);
        if (!ProcessImportedFunction(instance, index, func_index, module_name,
                                     import_name, value)) {
          return -1;
        }
        num_imported_functions++;
        break;
      }
      case kExternalTable: {
        uint32_t table_index = import.index;
        DCHECK_EQ(table_index, num_imported_tables);
        if (!ProcessImportedTable(instance, index, table_index, module_name,
                                  import_name, value)) {
          return -1;
        }
        num_imported_tables++;
        break;
      }
      case kExternalMemory: {
        if (!ProcessImportedMemory(instance, index, module_name, import_name,
                                   value)) {
          return -1;
        }
        break;
      }
      case kExternalGlobal: {
        if (!ProcessImportedGlobal(instance, index, import.index, module_name,
                                   import_name, value)) {
          return -1;
        }
        break;
      }
      case kExternalException: {
        if (!value->IsWasmExceptionObject()) {
          ReportLinkError("exception import requires a WebAssembly.Exception",
                          index, module_name, import_name);
          return -1;
        }
        Handle<WasmExceptionObject> imported_exception =
            Handle<WasmExceptionObject>::cast(value);
        if (!imported_exception->IsSignatureEqual(
                module_->exceptions[import.index].sig)) {
          ReportLinkError("imported exception does not match the expected type",
                          index, module_name, import_name);
          return -1;
        }
        Object exception_tag = imported_exception->exception_tag();
        DCHECK(instance->exceptions_table()->get(import.index)->IsUndefined());
        instance->exceptions_table()->set(import.index, exception_tag);
        exception_wrappers_[import.index] = imported_exception;
        break;
      }
      default:
        UNREACHABLE();
        break;
    }
  }
  return num_imported_functions;
}

template <typename T>
T* InstanceBuilder::GetRawGlobalPtr(const WasmGlobal& global) {
  return reinterpret_cast<T*>(raw_buffer_ptr(untagged_globals_, global.offset));
}

// Process initialization of globals.
void InstanceBuilder::InitGlobals() {
  for (auto global : module_->globals) {
    if (global.mutability && global.imported) {
      continue;
    }

    switch (global.init.kind) {
      case WasmInitExpr::kI32Const:
        WriteLittleEndianValue<int32_t>(GetRawGlobalPtr<int32_t>(global),
                                        global.init.val.i32_const);
        break;
      case WasmInitExpr::kI64Const:
        WriteLittleEndianValue<int64_t>(GetRawGlobalPtr<int64_t>(global),
                                        global.init.val.i64_const);
        break;
      case WasmInitExpr::kF32Const:
        WriteLittleEndianValue<float>(GetRawGlobalPtr<float>(global),
                                      global.init.val.f32_const);
        break;
      case WasmInitExpr::kF64Const:
        WriteLittleEndianValue<double>(GetRawGlobalPtr<double>(global),
                                       global.init.val.f64_const);
        break;
      case WasmInitExpr::kAnyRefConst:
        DCHECK(enabled_.anyref);
        if (global.imported) break;  // We already initialized imported globals.

        tagged_globals_->set(global.offset,
                             ReadOnlyRoots(isolate_).null_value(),
                             SKIP_WRITE_BARRIER);
        break;
      case WasmInitExpr::kGlobalIndex: {
        if (global.type == ValueType::kWasmAnyRef) {
          DCHECK(enabled_.anyref);
          int other_offset =
              module_->globals[global.init.val.global_index].offset;

          tagged_globals_->set(global.offset,
                               tagged_globals_->get(other_offset),
                               SKIP_WRITE_BARRIER);
        }
        // Initialize with another global.
        uint32_t new_offset = global.offset;
        uint32_t old_offset =
            module_->globals[global.init.val.global_index].offset;
        TRACE("init [globals+%u] = [globals+%d]\n", global.offset, old_offset);
        size_t size = (global.type == kWasmI64 || global.type == kWasmF64)
                          ? sizeof(double)
                          : sizeof(int32_t);
        memcpy(raw_buffer_ptr(untagged_globals_, new_offset),
               raw_buffer_ptr(untagged_globals_, old_offset), size);
        break;
      }
      case WasmInitExpr::kNone:
        // Happens with imported globals.
        break;
      default:
        UNREACHABLE();
        break;
    }
  }
}

// Allocate memory for a module instance as a new JSArrayBuffer.
Handle<JSArrayBuffer> InstanceBuilder::AllocateMemory(uint32_t initial_pages,
                                                      uint32_t maximum_pages) {
  if (initial_pages > max_mem_pages()) {
    thrower_->RangeError("Out of memory: wasm memory too large");
    return Handle<JSArrayBuffer>::null();
  }
  const bool is_shared_memory = module_->has_shared_memory && enabled_.threads;
  Handle<JSArrayBuffer> mem_buffer;
  if (is_shared_memory) {
    if (!NewSharedArrayBuffer(isolate_, initial_pages * kWasmPageSize,
                              maximum_pages * kWasmPageSize)
             .ToHandle(&mem_buffer)) {
      thrower_->RangeError("Out of memory: wasm shared memory");
    }
  } else {
    if (!NewArrayBuffer(isolate_, initial_pages * kWasmPageSize)
             .ToHandle(&mem_buffer)) {
      thrower_->RangeError("Out of memory: wasm memory");
    }
  }
  return mem_buffer;
}

bool InstanceBuilder::NeedsWrappers() const {
  if (module_->num_exported_functions > 0) return true;
  for (auto& table_instance : table_instances_) {
    if (!table_instance.js_functions.is_null()) return true;
  }
  for (auto& table : module_->tables) {
    if (table.exported) return true;
  }
  return false;
}

// Process the exports, creating wrappers for functions, tables, memories,
// globals, and exceptions.
void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
  Handle<FixedArray> export_wrappers(module_object_->export_wrappers(),
                                     isolate_);
  if (NeedsWrappers()) {
    // If an imported WebAssembly function gets exported, the exported function
    // has to be identical to to imported function. Therefore we cache all
    // imported WebAssembly functions in the instance.
    for (int index = 0, end = static_cast<int>(module_->import_table.size());
         index < end; ++index) {
      const WasmImport& import = module_->import_table[index];
      if (import.kind == kExternalFunction) {
        Handle<Object> value = sanitized_imports_[index].value;
        if (WasmExportedFunction::IsWasmExportedFunction(*value)) {
          WasmInstanceObject::SetWasmExportedFunction(
              isolate_, instance, import.index,
              Handle<WasmExportedFunction>::cast(value));
        }
      }
    }
  }

  Handle<JSObject> exports_object;
  bool is_asm_js = false;
  switch (module_->origin) {
    case kWasmOrigin: {
      // Create the "exports" object.
      exports_object = isolate_->factory()->NewJSObjectWithNullProto();
      break;
    }
    case kAsmJsOrigin: {
      Handle<JSFunction> object_function = Handle<JSFunction>(
          isolate_->native_context()->object_function(), isolate_);
      exports_object = isolate_->factory()->NewJSObject(object_function);
      is_asm_js = true;
      break;
    }
    default:
      UNREACHABLE();
  }
  instance->set_exports_object(*exports_object);

  Handle<String> single_function_name =
      isolate_->factory()->InternalizeUtf8String(AsmJs::kSingleFunctionName);

  PropertyDescriptor desc;
  desc.set_writable(is_asm_js);
  desc.set_enumerable(true);
  desc.set_configurable(is_asm_js);

  // Process each export in the export table.
  int export_index = 0;  // Index into {export_wrappers}.
  for (const WasmExport& exp : module_->export_table) {
    Handle<String> name = WasmModuleObject::ExtractUtf8StringFromModuleBytes(
                              isolate_, module_object_, exp.name)
                              .ToHandleChecked();
    Handle<JSObject> export_to;
    if (is_asm_js && exp.kind == kExternalFunction &&
        String::Equals(isolate_, name, single_function_name)) {
      export_to = instance;
    } else {
      export_to = exports_object;
    }

    switch (exp.kind) {
      case kExternalFunction: {
        // Wrap and export the code as a JSFunction.
        // TODO(wasm): reduce duplication with LoadElemSegment() further below
        const WasmFunction& function = module_->functions[exp.index];
        MaybeHandle<WasmExportedFunction> wasm_exported_function =
            WasmInstanceObject::GetWasmExportedFunction(isolate_, instance,
                                                        exp.index);
        if (wasm_exported_function.is_null()) {
          // Wrap the exported code as a JSFunction.
          Handle<Code> export_code =
              export_wrappers->GetValueChecked<Code>(isolate_, export_index);
          MaybeHandle<String> func_name;
          if (is_asm_js) {
            // For modules arising from asm.js, honor the names section.
            WireBytesRef func_name_ref = module_->LookupFunctionName(
                ModuleWireBytes(module_object_->native_module()->wire_bytes()),
                function.func_index);
            func_name = WasmModuleObject::ExtractUtf8StringFromModuleBytes(
                            isolate_, module_object_, func_name_ref)
                            .ToHandleChecked();
          }
          wasm_exported_function = WasmExportedFunction::New(
              isolate_, instance, func_name, function.func_index,
              static_cast<int>(function.sig->parameter_count()), export_code);
          WasmInstanceObject::SetWasmExportedFunction(
              isolate_, instance, exp.index,
              wasm_exported_function.ToHandleChecked());
        }
        desc.set_value(wasm_exported_function.ToHandleChecked());
        export_index++;
        break;
      }
      case kExternalTable: {
        // Export a table as a WebAssembly.Table object.
        TableInstance& table_instance = table_instances_[exp.index];
        const WasmTable& table = module_->tables[exp.index];
        if (table_instance.table_object.is_null()) {
          uint32_t maximum = table.has_maximum_size ? table.maximum_size
                                                    : FLAG_wasm_max_table_size;
          table_instance.table_object =
              WasmTableObject::New(isolate_, table.initial_size, maximum,
                                   &table_instance.js_functions);
        }
        instance->set_table_object(*table_instance.table_object);
        desc.set_value(table_instance.table_object);
        break;
      }
      case kExternalMemory: {
        // Export the memory as a WebAssembly.Memory object. A WasmMemoryObject
        // should already be available if the module has memory, since we always
        // create or import it when building an WasmInstanceObject.
        DCHECK(instance->has_memory_object());
        desc.set_value(
            Handle<WasmMemoryObject>(instance->memory_object(), isolate_));
        break;
      }
      case kExternalGlobal: {
        const WasmGlobal& global = module_->globals[exp.index];
        Handle<JSArrayBuffer> untagged_buffer;
        Handle<FixedArray> tagged_buffer;
        uint32_t offset;

        if (global.mutability && global.imported) {
          Handle<FixedArray> buffers_array(
              instance->imported_mutable_globals_buffers(), isolate_);
          if (global.type == kWasmAnyRef) {
            tagged_buffer = buffers_array->GetValueChecked<FixedArray>(
                isolate_, global.index);
            // For anyref globals we store the relative offset in the
            // imported_mutable_globals array instead of an absolute address.
            Address addr = instance->imported_mutable_globals()[global.index];
            DCHECK_LE(addr, static_cast<Address>(
                                std::numeric_limits<uint32_t>::max()));
            offset = static_cast<uint32_t>(addr);
          } else {
            untagged_buffer = buffers_array->GetValueChecked<JSArrayBuffer>(
                isolate_, global.index);
            Address global_addr =
                instance->imported_mutable_globals()[global.index];

            size_t buffer_size = untagged_buffer->byte_length();
            Address backing_store =
                reinterpret_cast<Address>(untagged_buffer->backing_store());
            CHECK(global_addr >= backing_store &&
                  global_addr < backing_store + buffer_size);
            offset = static_cast<uint32_t>(global_addr - backing_store);
          }
        } else {
          if (global.type == kWasmAnyRef) {
            tagged_buffer = handle(instance->tagged_globals_buffer(), isolate_);
          } else {
            untagged_buffer =
                handle(instance->untagged_globals_buffer(), isolate_);
          }
          offset = global.offset;
        }

        // Since the global's array untagged_buffer is always provided,
        // allocation should never fail.
        Handle<WasmGlobalObject> global_obj =
            WasmGlobalObject::New(isolate_, untagged_buffer, tagged_buffer,
                                  global.type, offset, global.mutability)
                .ToHandleChecked();
        desc.set_value(global_obj);
        break;
      }
      case kExternalException: {
        const WasmException& exception = module_->exceptions[exp.index];
        Handle<WasmExceptionObject> wrapper = exception_wrappers_[exp.index];
        if (wrapper.is_null()) {
          Handle<HeapObject> exception_tag(
              HeapObject::cast(instance->exceptions_table()->get(exp.index)),
              isolate_);
          wrapper =
              WasmExceptionObject::New(isolate_, exception.sig, exception_tag);
          exception_wrappers_[exp.index] = wrapper;
        }
        desc.set_value(wrapper);
        break;
      }
      default:
        UNREACHABLE();
        break;
    }

    v8::Maybe<bool> status = JSReceiver::DefineOwnProperty(
        isolate_, export_to, name, &desc, Just(kThrowOnError));
    if (!status.IsJust()) {
      DisallowHeapAllocation no_gc;
      TruncatedUserString<> trunc_name(name->GetCharVector<uint8_t>(no_gc));
      thrower_->LinkError("export of %.*s failed.", trunc_name.length(),
                          trunc_name.start());
      return;
    }
  }
  DCHECK_EQ(export_index, export_wrappers->length());

  if (module_->origin == kWasmOrigin) {
    v8::Maybe<bool> success =
        JSReceiver::SetIntegrityLevel(exports_object, FROZEN, kDontThrow);
    DCHECK(success.FromMaybe(false));
    USE(success);
  }
}

void InstanceBuilder::InitializeTables(Handle<WasmInstanceObject> instance) {
  size_t table_count = module_->tables.size();
  for (size_t index = 0; index < table_count; ++index) {
    const WasmTable& table = module_->tables[index];

    if (!instance->has_indirect_function_table() &&
        table.type == kWasmAnyFunc) {
      WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
          instance, table.initial_size);
      table_instances_[index].table_size = table.initial_size;
    }
  }
}

bool LoadElemSegmentImpl(Isolate* isolate, Handle<WasmInstanceObject> instance,
                         const TableInstance& table_instance,
                         JSToWasmWrapperCache* js_to_wasm_cache,
                         const WasmElemSegment& elem_segment, uint32_t dst,
                         uint32_t src, size_t count) {
  if (!IsInBounds(dst, count, table_instance.table_size)) return false;
  if (!IsInBounds(src, count, elem_segment.entries.size())) return false;

  const WasmModule* module = instance->module();
  for (uint32_t i = 0; i < count; ++i) {
    uint32_t func_index = elem_segment.entries[src + i];
    const WasmFunction* function = &module->functions[func_index];
    int entry_index = static_cast<int>(dst + i);

    // Update the local dispatch table first.
    uint32_t sig_id = module->signature_ids[function->sig_index];
    IndirectFunctionTableEntry(instance, entry_index)
        .Set(sig_id, instance, func_index);

    if (!table_instance.table_object.is_null()) {
      // Update the table object's other dispatch tables.
      MaybeHandle<WasmExportedFunction> wasm_exported_function =
          WasmInstanceObject::GetWasmExportedFunction(isolate, instance,
                                                      func_index);
      if (wasm_exported_function.is_null()) {
        // No JSFunction entry yet exists for this function. Create one.
        // TODO(titzer): We compile JS->wasm wrappers for functions are
        // not exported but are in an exported table. This should be done
        // at module compile time and cached instead.

        Handle<Code> wrapper_code =
            js_to_wasm_cache->GetOrCompileJSToWasmWrapper(
                isolate, function->sig, function->imported);
        MaybeHandle<String> func_name;
        if (module->origin == kAsmJsOrigin) {
          // For modules arising from asm.js, honor the names section.
          auto module_object =
              Handle<WasmModuleObject>(instance->module_object(), isolate);
          WireBytesRef func_name_ref = module->LookupFunctionName(
              ModuleWireBytes(module_object->native_module()->wire_bytes()),
              func_index);
          func_name = WasmModuleObject::ExtractUtf8StringFromModuleBytes(
                          isolate, module_object, func_name_ref)
                          .ToHandleChecked();
        }
        wasm_exported_function = WasmExportedFunction::New(
            isolate, instance, func_name, func_index,
            static_cast<int>(function->sig->parameter_count()), wrapper_code);
        WasmInstanceObject::SetWasmExportedFunction(
            isolate, instance, func_index,
            wasm_exported_function.ToHandleChecked());
      }
      table_instance.js_functions->set(
          entry_index, *wasm_exported_function.ToHandleChecked());
      // UpdateDispatchTables() updates all other dispatch tables, since
      // we have not yet added the dispatch table we are currently building.
      WasmTableObject::UpdateDispatchTables(
          isolate, table_instance.table_object, entry_index, function->sig,
          instance, func_index);
    }
  }
  return true;
}

void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
  for (auto& elem_segment : module_->elem_segments) {
    // Passive segments are not copied during instantiation.
    if (!elem_segment.active) continue;

    uint32_t dst = EvalUint32InitExpr(instance, elem_segment.offset);
    uint32_t src = 0;
    size_t count = elem_segment.entries.size();

    bool success = LoadElemSegmentImpl(
        isolate_, instance, table_instances_[elem_segment.table_index],
        &js_to_wasm_cache_, elem_segment, dst, src, count);
    CHECK(success);
  }

  int table_count = static_cast<int>(module_->tables.size());
  for (int index = 0; index < table_count; ++index) {
    TableInstance& table_instance = table_instances_[index];

    // Add the new dispatch table at the end to avoid redundant lookups.
    if (!table_instance.table_object.is_null()) {
      WasmTableObject::AddDispatchTable(isolate_, table_instance.table_object,
                                        instance, index);
    }
  }
}

void InstanceBuilder::InitializeExceptions(
    Handle<WasmInstanceObject> instance) {
  Handle<FixedArray> exceptions_table(instance->exceptions_table(), isolate_);
  for (int index = 0; index < exceptions_table->length(); ++index) {
    if (!exceptions_table->get(index)->IsUndefined(isolate_)) continue;
    Handle<WasmExceptionTag> exception_tag =
        WasmExceptionTag::New(isolate_, index);
    exceptions_table->set(index, *exception_tag);
  }
}

bool LoadElemSegment(Isolate* isolate, Handle<WasmInstanceObject> instance,
                     uint32_t table_index, uint32_t segment_index, uint32_t dst,
                     uint32_t src, uint32_t count) {
  JSToWasmWrapperCache js_to_wasm_cache;

  Handle<WasmTableObject> table_object;
  Handle<FixedArray> js_functions;
  if (instance->has_table_object()) {
    table_object = Handle<WasmTableObject>(instance->table_object(), isolate);
    js_functions = Handle<FixedArray>(table_object->elements(), isolate);
  }

  TableInstance table_instance = {table_object, js_functions,
                                  instance->indirect_function_table_size()};

  auto& elem_segment = instance->module()->elem_segments[segment_index];
  return LoadElemSegmentImpl(isolate, instance, table_instance,
                             &js_to_wasm_cache, elem_segment, dst, src, count);
}

}  // namespace wasm
}  // namespace internal
}  // namespace v8

#undef TRACE