// Copyright 2015 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/wasm-module.h"

#include <memory>

#include "src/api/api-inl.h"
#include "src/compiler/wasm-compiler.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/objects.h"
#include "src/wasm/jump-table-assembler.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/std-object-sizes.h"
#include "src/wasm/wasm-code-manager.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-init-expr.h"
#include "src/wasm/wasm-js.h"
#include "src/wasm/wasm-module-builder.h"  // For {ZoneBuffer}.
#include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-result.h"
#include "src/wasm/wasm-subtyping.h"

namespace v8::internal::wasm {

void UpdateComputedInformation(WasmMemory* memory, ModuleOrigin origin) {
  const uintptr_t platform_max_pages =
      memory->is_memory64() ? wasm::max_mem64_pages() : wasm::max_mem32_pages();
  memory->min_memory_size = static_cast<uintptr_t>(std::min<uint64_t>(
                                platform_max_pages, memory->initial_pages)) *
                            kWasmPageSize;
  memory->max_memory_size = static_cast<uintptr_t>(std::min<uint64_t>(
                                platform_max_pages, memory->maximum_pages)) *
                            kWasmPageSize;

  if (!v8_flags.wasm_bounds_checks) {
    memory->bounds_checks = kNoBoundsChecks;
  } else if (v8_flags.wasm_enforce_bounds_checks) {
    // Explicit bounds checks requested via flag (for testing).
    memory->bounds_checks = kExplicitBoundsChecks;
  } else if (origin != kWasmOrigin) {
    // Asm.js modules can't use trap handling.
    memory->bounds_checks = kExplicitBoundsChecks;
  } else if (memory->is_memory64() && !v8_flags.wasm_memory64_trap_handling) {
    memory->bounds_checks = kExplicitBoundsChecks;
  } else if (trap_handler::IsTrapHandlerEnabled()) {
    if constexpr (kSystemPointerSize == 4) UNREACHABLE();
    memory->bounds_checks = kTrapHandler;
  } else {
    // If the trap handler is not enabled, fall back to explicit bounds checks.
    memory->bounds_checks = kExplicitBoundsChecks;
  }
}

// Ensure that the max subtyping depth can be stored in the TypeDefinition.
static_assert(
    kV8MaxRttSubtypingDepth <=
    std::numeric_limits<decltype(TypeDefinition().subtyping_depth)>::max());

template <class Value>
void AdaptiveMap<Value>::FinishInitialization() {
  uint32_t count = 0;
  uint32_t max = 0;
  DCHECK_EQ(mode_, kInitializing);
  for (const auto& entry : *map_) {
    count++;
    max = std::max(max, entry.first);
  }
  if (count >= (max + 1) / kLoadFactor) {
    mode_ = kDense;
    vector_.resize(max + 1);
    for (auto& entry : *map_) {
      vector_[entry.first] = std::move(entry.second);
    }
    map_.reset();
  } else {
    mode_ = kSparse;
  }
}
template void NameMap::FinishInitialization();
template void IndirectNameMap::FinishInitialization();

WireBytesRef LazilyGeneratedNames::LookupFunctionName(
    ModuleWireBytes wire_bytes, uint32_t function_index) {
  base::MutexGuard lock(&mutex_);
  if (!has_functions_) {
    has_functions_ = true;
    DecodeFunctionNames(wire_bytes.module_bytes(), function_names_);
  }
  const WireBytesRef* result = function_names_.Get(function_index);
  if (!result) return WireBytesRef();
  return *result;
}

bool LazilyGeneratedNames::Has(uint32_t function_index) {
  DCHECK(has_functions_);
  base::MutexGuard lock(&mutex_);
  return function_names_.Get(function_index) != nullptr;
}

// static
int GetWasmFunctionOffset(const WasmModule* module, uint32_t func_index) {
  const std::vector<WasmFunction>& functions = module->functions;
  if (static_cast<uint32_t>(func_index) >= functions.size()) return -1;
  DCHECK_GE(kMaxInt, functions[func_index].code.offset());
  return static_cast<int>(functions[func_index].code.offset());
}

// static
int GetNearestWasmFunction(const WasmModule* module, uint32_t byte_offset) {
  const std::vector<WasmFunction>& functions = module->functions;

  // Binary search for a function containing the given position.
  int left = 0;                                    // inclusive
  int right = static_cast<int>(functions.size());  // exclusive
  if (right == 0) return -1;
  while (right - left > 1) {
    int mid = left + (right - left) / 2;
    if (functions[mid].code.offset() <= byte_offset) {
      left = mid;
    } else {
      right = mid;
    }
  }

  return left;
}

// static
int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset) {
  int func_index = GetNearestWasmFunction(module, byte_offset);

  if (func_index >= 0) {
    // If the found function does not contain the given position, return -1.
    const WasmFunction& func = module->functions[func_index];
    if (byte_offset < func.code.offset() ||
        byte_offset >= func.code.end_offset()) {
      return -1;
    }
  }
  return func_index;
}

int GetSubtypingDepth(const WasmModule* module, ModuleTypeIndex type_index) {
  DCHECK_LT(type_index.index, module->types.size());
  int depth = module->type(type_index).subtyping_depth;
  DCHECK_LE(depth, kV8MaxRttSubtypingDepth);
  return depth;
}

void LazilyGeneratedNames::AddForTesting(int function_index,
                                         WireBytesRef name) {
  base::MutexGuard lock(&mutex_);
  function_names_.Put(function_index, name);
}

AsmJsOffsetInformation::AsmJsOffsetInformation(
    base::Vector<const uint8_t> encoded_offsets)
    : encoded_offsets_(base::OwnedCopyOf(encoded_offsets)) {}

AsmJsOffsetInformation::~AsmJsOffsetInformation() = default;

int AsmJsOffsetInformation::GetSourcePosition(int declared_func_index,
                                              int byte_offset,
                                              bool is_at_number_conversion) {
  EnsureDecodedOffsets();

  DCHECK_LE(0, declared_func_index);
  DCHECK_GT(decoded_offsets_->functions.size(), declared_func_index);
  std::vector<AsmJsOffsetEntry>& function_offsets =
      decoded_offsets_->functions[declared_func_index].entries;

  auto byte_offset_less = [](const AsmJsOffsetEntry& a,
                             const AsmJsOffsetEntry& b) {
    return a.byte_offset < b.byte_offset;
  };
  SLOW_DCHECK(std::is_sorted(function_offsets.begin(), function_offsets.end(),
                             byte_offset_less));
  // If there are no positions recorded, map offset 0 (for function entry) to
  // position 0.
  if (function_offsets.empty() && byte_offset == 0) return 0;
  auto it =
      std::lower_bound(function_offsets.begin(), function_offsets.end(),
                       AsmJsOffsetEntry{byte_offset, 0, 0}, byte_offset_less);
  DCHECK_NE(function_offsets.end(), it);
  DCHECK_EQ(byte_offset, it->byte_offset);
  return is_at_number_conversion ? it->source_position_number_conversion
                                 : it->source_position_call;
}

std::pair<int, int> AsmJsOffsetInformation::GetFunctionOffsets(
    int declared_func_index) {
  EnsureDecodedOffsets();

  DCHECK_LE(0, declared_func_index);
  DCHECK_GT(decoded_offsets_->functions.size(), declared_func_index);
  AsmJsOffsetFunctionEntries& function_info =
      decoded_offsets_->functions[declared_func_index];

  return {function_info.start_offset, function_info.end_offset};
}

void AsmJsOffsetInformation::EnsureDecodedOffsets() {
  base::MutexGuard mutex_guard(&mutex_);
  DCHECK_EQ(encoded_offsets_ == nullptr, decoded_offsets_ != nullptr);

  if (decoded_offsets_) return;
  AsmJsOffsetsResult result =
      wasm::DecodeAsmJsOffsets(encoded_offsets_.as_vector());
  decoded_offsets_ = std::make_unique<AsmJsOffsets>(std::move(result).value());
  encoded_offsets_.ReleaseData();
}

// Get a string stored in the module bytes representing a name.
WasmName ModuleWireBytes::GetNameOrNull(WireBytesRef ref) const {
  if (!ref.is_set()) return {nullptr, 0};  // no name.
  DCHECK(BoundsCheck(ref));
  return WasmName::cast(
      module_bytes_.SubVector(ref.offset(), ref.end_offset()));
}

// Get a string stored in the module bytes representing a function name.
WasmName ModuleWireBytes::GetNameOrNull(int func_index,
                                        const WasmModule* module) const {
  return GetNameOrNull(
      module->lazily_generated_names.LookupFunctionName(*this, func_index));
}

std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name) {
  os << "#" << name.func_index_;
  if (!name.name_.empty()) {
    if (name.name_.begin()) {
      os << ":";
      os.write(name.name_.begin(), name.name_.size());
    }
  } else {
    os << "?";
  }
  return os;
}

WasmModule::WasmModule(ModuleOrigin origin)
    : signature_zone(GetWasmEngine()->allocator(), "signature zone"),
      origin(origin) {}

uint64_t WasmModule::signature_hash(const TypeCanonicalizer* type_canonicalizer,
                                    uint32_t function_index) const {
  if (function_index >= functions.size()) {
    return kInvalidWasmSignatureHash;
  }

  CanonicalTypeIndex canonical_type_id =
      canonical_sig_id(functions[function_index].sig_index);
  return type_canonicalizer->LookupFunctionSignature(canonical_type_id)
      ->signature_hash();
}

bool IsWasmCodegenAllowed(Isolate* isolate,
                          DirectHandle<NativeContext> context) {
  // TODO(wasm): Once wasm has its own CSP policy, we should introduce a
  // separate callback that includes information about the module about to be
  // compiled. For the time being, pass an empty string as placeholder for the
  // sources.
  if (auto wasm_codegen_callback = isolate->allow_wasm_code_gen_callback()) {
    return wasm_codegen_callback(
        v8::Utils::ToLocal(context),
        v8::Utils::ToLocal(isolate->factory()->empty_string()));
  }
  return true;
}

DirectHandle<String> ErrorStringForCodegen(Isolate* isolate,
                                           DirectHandle<Context> context) {
  DirectHandle<Object> error = context->ErrorMessageForWasmCodeGeneration();
  DCHECK(!error.is_null());
  return Object::NoSideEffectsToString(isolate, error);
}

namespace {

// Converts the given {type} into a string representation that can be used in
// reflective functions. Should be kept in sync with the {GetValueType} helper.
DirectHandle<String> ToValueTypeString(Isolate* isolate, ValueType type) {
  return isolate->factory()->InternalizeUtf8String(base::VectorOf(type.name()));
}
}  // namespace

DirectHandle<JSObject> GetTypeForFunction(Isolate* isolate,
                                          const FunctionSig* sig,
                                          bool for_exception) {
  Factory* factory = isolate->factory();

  // Extract values for the {ValueType[]} arrays.
  int param_index = 0;
  int param_count = static_cast<int>(sig->parameter_count());
  DirectHandle<FixedArray> param_values = factory->NewFixedArray(param_count);
  for (ValueType type : sig->parameters()) {
    DirectHandle<String> type_value = ToValueTypeString(isolate, type);
    param_values->set(param_index++, *type_value);
  }

  // Create the resulting {FunctionType} object.
  DirectHandle<JSFunction> object_function = isolate->object_function();
  DirectHandle<JSObject> object = factory->NewJSObject(object_function);
  DirectHandle<JSArray> params = factory->NewJSArrayWithElements(param_values);
  DirectHandle<String> params_string =
      factory->InternalizeUtf8String("parameters");
  DirectHandle<String> results_string =
      factory->InternalizeUtf8String("results");
  JSObject::AddProperty(isolate, object, params_string, params, NONE);

  // Now add the result types if needed.
  if (for_exception) {
    DCHECK_EQ(sig->returns().size(), 0);
  } else {
    int result_index = 0;
    int result_count = static_cast<int>(sig->return_count());
    DirectHandle<FixedArray> result_values =
        factory->NewFixedArray(result_count);
    for (ValueType type : sig->returns()) {
      DirectHandle<String> type_value = ToValueTypeString(isolate, type);
      result_values->set(result_index++, *type_value);
    }
    DirectHandle<JSArray> results =
        factory->NewJSArrayWithElements(result_values);
    JSObject::AddProperty(isolate, object, results_string, results, NONE);
  }

  return object;
}

DirectHandle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable,
                                        ValueType type) {
  Factory* factory = isolate->factory();

  DirectHandle<JSFunction> object_function = isolate->object_function();
  DirectHandle<JSObject> object = factory->NewJSObject(object_function);
  DirectHandle<String> mutable_string =
      factory->InternalizeUtf8String("mutable");
  DirectHandle<String> value_string = factory->value_string();
  JSObject::AddProperty(isolate, object, mutable_string,
                        factory->ToBoolean(is_mutable), NONE);
  JSObject::AddProperty(isolate, object, value_string,
                        ToValueTypeString(isolate, type), NONE);

  return object;
}

DirectHandle<JSObject> GetTypeForMemory(Isolate* isolate, uint32_t min_size,
                                        std::optional<uint64_t> max_size,
                                        bool shared, AddressType address_type) {
  Factory* factory = isolate->factory();

  DirectHandle<JSFunction> object_function = isolate->object_function();
  DirectHandle<JSObject> object = factory->NewJSObject(object_function);
  DirectHandle<String> minimum_string =
      factory->InternalizeUtf8String("minimum");
  DirectHandle<String> maximum_string =
      factory->InternalizeUtf8String("maximum");
  DirectHandle<String> shared_string = factory->InternalizeUtf8String("shared");
  DirectHandle<String> address_string =
      factory->InternalizeUtf8String("address");
  JSObject::AddProperty(isolate, object, minimum_string,
                        factory->NewNumberFromUint(min_size), NONE);
  if (max_size.has_value()) {
    DirectHandle<UnionOf<Smi, HeapNumber, BigInt>> max;
    if (address_type == AddressType::kI32) {
      DCHECK_GE(kMaxUInt32, *max_size);
      max = factory->NewNumberFromUint(static_cast<uint32_t>(*max_size));
    } else {
      max = BigInt::FromUint64(isolate, *max_size);
    }
    JSObject::AddProperty(isolate, object, maximum_string, max, NONE);
  }
  JSObject::AddProperty(isolate, object, shared_string,
                        factory->ToBoolean(shared), NONE);

  JSObject::AddProperty(
      isolate, object, address_string,
      factory->InternalizeUtf8String(AddressTypeToStr(address_type)), NONE);

  return object;
}

DirectHandle<JSObject> GetTypeForTable(Isolate* isolate, ValueType type,
                                       uint32_t min_size,
                                       std::optional<uint64_t> max_size,
                                       AddressType address_type) {
  Factory* factory = isolate->factory();

  DirectHandle<String> element =
      factory->InternalizeUtf8String(base::VectorOf(type.name()));

  DirectHandle<JSFunction> object_function = isolate->object_function();
  DirectHandle<JSObject> object = factory->NewJSObject(object_function);
  DirectHandle<String> element_string = factory->element_string();
  DirectHandle<String> minimum_string =
      factory->InternalizeUtf8String("minimum");
  DirectHandle<String> maximum_string =
      factory->InternalizeUtf8String("maximum");
  DirectHandle<String> address_string =
      factory->InternalizeUtf8String("address");
  JSObject::AddProperty(isolate, object, element_string, element, NONE);
  JSObject::AddProperty(isolate, object, minimum_string,
                        factory->NewNumberFromUint(min_size), NONE);
  if (max_size.has_value()) {
    DirectHandle<UnionOf<Smi, HeapNumber, BigInt>> max;
    if (address_type == AddressType::kI32) {
      DCHECK_GE(kMaxUInt32, *max_size);
      max = factory->NewNumberFromUint(static_cast<uint32_t>(*max_size));
    } else {
      max = BigInt::FromUint64(isolate, *max_size);
    }
    JSObject::AddProperty(isolate, object, maximum_string, max, NONE);
  }
  JSObject::AddProperty(
      isolate, object, address_string,
      factory->InternalizeUtf8String(AddressTypeToStr(address_type)), NONE);

  return object;
}

DirectHandle<JSArray> GetImports(Isolate* isolate,
                                 DirectHandle<WasmModuleObject> module_object) {
  auto enabled_features = i::wasm::WasmEnabledFeatures::FromIsolate(isolate);
  Factory* factory = isolate->factory();

  DirectHandle<String> module_string = factory->InternalizeUtf8String("module");
  DirectHandle<String> name_string = factory->name_string();
  DirectHandle<String> kind_string = factory->InternalizeUtf8String("kind");
  DirectHandle<String> type_string = factory->InternalizeUtf8String("type");

  DirectHandle<String> function_string = factory->function_string();
  DirectHandle<String> table_string = factory->InternalizeUtf8String("table");
  DirectHandle<String> memory_string = factory->InternalizeUtf8String("memory");
  DirectHandle<String> global_string = factory->global_string();
  DirectHandle<String> tag_string = factory->InternalizeUtf8String("tag");

  // Create the result array.
  NativeModule* native_module = module_object->native_module();
  const WasmModule* module = native_module->module();
  base::Vector<const uint8_t> wire_bytes = native_module->wire_bytes();
  int num_imports = static_cast<int>(module->import_table.size());
  DirectHandle<JSArray> array_object =
      factory->NewJSArray(PACKED_ELEMENTS, 0, 0);
  DirectHandle<FixedArray> storage = factory->NewFixedArray(num_imports);
  JSArray::SetContent(isolate, array_object, storage);

  DirectHandle<JSFunction> object_function = DirectHandle<JSFunction>(
      isolate->native_context()->object_function(), isolate);

  // Populate the result array.
  const WellKnownImportsList& well_known_imports =
      module->type_feedback.well_known_imports;
  const std::string& magic_string_constants =
      native_module->compile_imports().constants_module();
  const bool has_magic_string_constants =
      native_module->compile_imports().contains(
          CompileTimeImport::kStringConstants);

  int cursor = 0;
  for (int index = 0; index < num_imports; ++index) {
    const WasmImport& import = module->import_table[index];

    DirectHandle<JSObject> entry = factory->NewJSObject(object_function);

    DirectHandle<String> import_kind;
    DirectHandle<JSObject> type_value;
    switch (import.kind) {
      case kExternalFunction:
      case kExternalExactFunction:
        if (IsCompileTimeImport(well_known_imports.get(import.index))) {
          continue;
        }
        if (enabled_features.has_type_reflection()) {
          auto& func = module->functions[import.index];
          type_value = GetTypeForFunction(isolate, func.sig);
        }
        // Since {kExternalExactFunction} is still a function import, it
        // uses the string "function" here.
        import_kind = function_string;
        break;
      case kExternalTable:
        if (enabled_features.has_type_reflection()) {
          auto& table = module->tables[import.index];
          std::optional<uint32_t> maximum_size;
          if (table.has_maximum_size) maximum_size.emplace(table.maximum_size);
          type_value = GetTypeForTable(isolate, table.type, table.initial_size,
                                       maximum_size, table.address_type);
        }
        import_kind = table_string;
        break;
      case kExternalMemory:
        if (enabled_features.has_type_reflection()) {
          auto& memory = module->memories[import.index];
          std::optional<uint32_t> maximum_size;
          if (memory.has_maximum_pages) {
            maximum_size.emplace(memory.maximum_pages);
          }
          type_value =
              GetTypeForMemory(isolate, memory.initial_pages, maximum_size,
                               memory.is_shared, memory.address_type);
        }
        import_kind = memory_string;
        break;
      case kExternalGlobal:
        if (has_magic_string_constants &&
            import.module_name.length() == magic_string_constants.size() &&
            std::equal(magic_string_constants.begin(),
                       magic_string_constants.end(),
                       wire_bytes.begin() + import.module_name.offset())) {
          continue;
        }
        if (enabled_features.has_type_reflection()) {
          auto& global = module->globals[import.index];
          type_value =
              GetTypeForGlobal(isolate, global.mutability, global.type);
        }
        import_kind = global_string;
        break;
      case kExternalTag:
        import_kind = tag_string;
        break;
    }
    DCHECK(!import_kind.is_null());

    DirectHandle<String> import_module =
        WasmModuleObject::ExtractUtf8StringFromModuleBytes(
            isolate, wire_bytes, import.module_name, kInternalize);

    DirectHandle<String> import_name =
        WasmModuleObject::ExtractUtf8StringFromModuleBytes(
            isolate, wire_bytes, import.field_name, kInternalize);

    JSObject::AddProperty(isolate, entry, module_string, import_module, NONE);
    JSObject::AddProperty(isolate, entry, name_string, import_name, NONE);
    JSObject::AddProperty(isolate, entry, kind_string, import_kind, NONE);
    if (!type_value.is_null()) {
      JSObject::AddProperty(isolate, entry, type_string, type_value, NONE);
    }

    storage->set(cursor++, *entry);
  }

  array_object->set_length(Smi::FromInt(cursor));
  // Make sure that values after the cursor are holes.
  for (int index = cursor; index < num_imports; ++index) {
    storage->set_the_hole(isolate, cursor++);
  }
  JSObject::ValidateElements(isolate, *array_object);
  return array_object;
}

DirectHandle<JSArray> GetExports(Isolate* isolate,
                                 DirectHandle<WasmModuleObject> module_object) {
  auto enabled_features = i::wasm::WasmEnabledFeatures::FromIsolate(isolate);
  Factory* factory = isolate->factory();

  DirectHandle<String> name_string = factory->name_string();
  DirectHandle<String> kind_string = factory->InternalizeUtf8String("kind");
  DirectHandle<String> type_string = factory->InternalizeUtf8String("type");

  DirectHandle<String> function_string = factory->function_string();
  DirectHandle<String> table_string = factory->InternalizeUtf8String("table");
  DirectHandle<String> memory_string = factory->InternalizeUtf8String("memory");
  DirectHandle<String> global_string = factory->global_string();
  DirectHandle<String> tag_string = factory->InternalizeUtf8String("tag");

  // Create the result array.
  NativeModule* native_module = module_object->native_module();
  const WasmModule* module = native_module->module();
  int num_exports = static_cast<int>(module->export_table.size());
  DirectHandle<JSArray> array_object =
      factory->NewJSArray(PACKED_ELEMENTS, 0, 0);
  DirectHandle<FixedArray> storage = factory->NewFixedArray(num_exports);
  JSArray::SetContent(isolate, array_object, storage);
  DCHECK_EQ(array_object->length(), Smi::FromInt(num_exports));

  DirectHandle<JSFunction> object_function = DirectHandle<JSFunction>(
      isolate->native_context()->object_function(), isolate);

  // Populate the result array.
  for (int index = 0; index < num_exports; ++index) {
    const WasmExport& exp = module->export_table[index];

    DirectHandle<String> export_kind;
    DirectHandle<JSObject> type_value;
    switch (exp.kind) {
      case kExternalFunction:
        if (enabled_features.has_type_reflection()) {
          auto& func = module->functions[exp.index];
          type_value = GetTypeForFunction(isolate, func.sig);
        }
        export_kind = function_string;
        break;
      case kExternalTable:
        if (enabled_features.has_type_reflection()) {
          auto& table = module->tables[exp.index];
          std::optional<uint32_t> maximum_size;
          if (table.has_maximum_size) maximum_size.emplace(table.maximum_size);
          type_value = GetTypeForTable(isolate, table.type, table.initial_size,
                                       maximum_size, table.address_type);
        }
        export_kind = table_string;
        break;
      case kExternalMemory:
        if (enabled_features.has_type_reflection()) {
          auto& memory = module->memories[exp.index];
          std::optional<uint32_t> maximum_size;
          if (memory.has_maximum_pages) {
            maximum_size.emplace(memory.maximum_pages);
          }
          type_value =
              GetTypeForMemory(isolate, memory.initial_pages, maximum_size,
                               memory.is_shared, memory.address_type);
        }
        export_kind = memory_string;
        break;
      case kExternalGlobal:
        if (enabled_features.has_type_reflection()) {
          auto& global = module->globals[exp.index];
          type_value =
              GetTypeForGlobal(isolate, global.mutability, global.type);
        }
        export_kind = global_string;
        break;
      case kExternalTag:
        export_kind = tag_string;
        break;
      case kExternalExactFunction:
      default:
        UNREACHABLE();
    }

    DirectHandle<JSObject> entry = factory->NewJSObject(object_function);

    DirectHandle<String> export_name =
        WasmModuleObject::ExtractUtf8StringFromModuleBytes(
            isolate, native_module->wire_bytes(), exp.name, kNoInternalize);

    JSObject::AddProperty(isolate, entry, name_string, export_name, NONE);
    JSObject::AddProperty(isolate, entry, kind_string, export_kind, NONE);
    if (!type_value.is_null()) {
      JSObject::AddProperty(isolate, entry, type_string, type_value, NONE);
    }

    storage->set(index, *entry);
  }

  JSObject::ValidateElements(isolate, *array_object);
  return array_object;
}

DirectHandle<JSArray> GetCustomSections(
    Isolate* isolate, DirectHandle<WasmModuleObject> module_object,
    DirectHandle<String> name, ErrorThrower* thrower) {
  Factory* factory = isolate->factory();

  base::Vector<const uint8_t> wire_bytes =
      module_object->native_module()->wire_bytes();
  std::vector<CustomSectionOffset> custom_sections =
      DecodeCustomSections(wire_bytes);

  DirectHandleVector<Object> matching_sections(isolate);

  // Gather matching sections.
  for (auto& section : custom_sections) {
    DirectHandle<String> section_name =
        WasmModuleObject::ExtractUtf8StringFromModuleBytes(
            isolate, wire_bytes, section.name, kNoInternalize);

    if (!name->Equals(*section_name)) continue;

    // Make a copy of the payload data in the section.
    size_t size = section.payload.length();
    MaybeDirectHandle<JSArrayBuffer> result =
        isolate->factory()->NewJSArrayBufferAndBackingStore(
            size, InitializedFlag::kUninitialized);
    DirectHandle<JSArrayBuffer> array_buffer;
    if (!result.ToHandle(&array_buffer)) {
      thrower->RangeError("out of memory allocating custom section data");
      return DirectHandle<JSArray>();
    }
    memcpy(array_buffer->backing_store(),
           wire_bytes.begin() + section.payload.offset(),
           section.payload.length());

    matching_sections.push_back(array_buffer);
  }

  int num_custom_sections = static_cast<int>(matching_sections.size());
  DirectHandle<JSArray> array_object =
      factory->NewJSArray(PACKED_ELEMENTS, 0, 0);
  DirectHandle<FixedArray> storage =
      factory->NewFixedArray(num_custom_sections);
  JSArray::SetContent(isolate, array_object, storage);
  DCHECK_EQ(array_object->length(), Smi::FromInt(num_custom_sections));

  for (int i = 0; i < num_custom_sections; i++) {
    storage->set(i, *matching_sections[i]);
  }

  JSObject::ValidateElements(isolate, *array_object);
  return array_object;
}

// Get the source position from a given function index and wire bytes offset
// (relative to the function entry), for either asm.js or pure Wasm modules.
int GetSourcePosition(const WasmModule* module, uint32_t func_index,
                      uint32_t byte_offset, bool is_at_number_conversion) {
  DCHECK_EQ(is_asmjs_module(module),
            module->asm_js_offset_information != nullptr);
  if (!is_asmjs_module(module)) {
    // For non-asm.js modules, we just add the function's start offset
    // to make a module-relative position.
    return byte_offset + GetWasmFunctionOffset(module, func_index);
  }

  // asm.js modules have an additional offset table that must be searched.
  return module->asm_js_offset_information->GetSourcePosition(
      declared_function_index(module, func_index), byte_offset,
      is_at_number_conversion);
}

size_t WasmModule::EstimateStoredSize() const {
  UPDATE_WHEN_CLASS_CHANGES(WasmModule,
#if V8_ENABLE_DRUMBRAKE
                            952
#else   // V8_ENABLE_DRUMBRAKE
                            920
#endif  // V8_ENABLE_DRUMBRAKE
  );
  return sizeof(WasmModule) +                            // --
         signature_zone.allocation_size_for_tracing() +  // --
         ContentSize(types) +                            // --
         ContentSize(isorecursive_canonical_type_ids) +  // --
         ContentSize(functions) +                        // --
         ContentSize(globals) +                          // --
         ContentSize(data_segments) +                    // --
         ContentSize(tables) +                           // --
         ContentSize(memories) +                         // --
         ContentSize(import_table) +                     // --
         ContentSize(export_table) +                     // --
         ContentSize(tags) +                             // --
         ContentSize(stringref_literals) +               // --
         ContentSize(elem_segments) +                    // --
         ContentSize(branch_hints) +                     // --
         ContentSize(compilation_priorities) +           // --
         ContentSize(instruction_frequencies) +          // --
         ContentSize(call_targets) +                     // --
         ContentSize(inst_traces) +                      // --
         (num_declared_functions + 7) / 8;               // validated_functions
}

template <class Value>
size_t AdaptiveMap<Value>::EstimateCurrentMemoryConsumption() const {
  UNREACHABLE();  // Explicit implementations below.
}

template <>
size_t NameMap::EstimateCurrentMemoryConsumption() const {
  size_t result = ContentSize(vector_);
  if (map_) result += ContentSize(*map_);
  return result;
}

size_t LazilyGeneratedNames::EstimateCurrentMemoryConsumption() const {
  base::MutexGuard lock(&mutex_);
  return function_names_.EstimateCurrentMemoryConsumption();
}

template <>
size_t IndirectNameMap::EstimateCurrentMemoryConsumption() const {
  size_t result = ContentSize(vector_);
  for (const auto& inner_map : vector_) {
    result += inner_map.EstimateCurrentMemoryConsumption();
  }
  if (map_) {
    result += ContentSize(*map_);
    for (const auto& [outer_index, inner_map] : *map_) {
      result += inner_map.EstimateCurrentMemoryConsumption();
    }
  }
  return result;
}

size_t TypeFeedbackStorage::EstimateCurrentMemoryConsumption() const {
  UPDATE_WHEN_CLASS_CHANGES(TypeFeedbackStorage, 112);
  UPDATE_WHEN_CLASS_CHANGES(FunctionTypeFeedback, 48);
  // Not including sizeof(TFS) because that's contained in sizeof(WasmModule).
  base::MutexGuard guard(&mutex);
  size_t result = ContentSize(feedback_for_function);
  for (const auto& [func_idx, feedback] : feedback_for_function) {
    result += ContentSize(feedback.feedback_vector);
    result += feedback.call_targets.size() * sizeof(uint32_t);
  }
  result += ContentSize(deopt_count_for_function);
  // The size of {well_known_imports} can only be estimated at the WasmModule
  // level.
  if (v8_flags.trace_wasm_offheap_memory) {
    PrintF("TypeFeedback: %zu\n", result);
  }
  return result;
}

size_t WasmModule::EstimateCurrentMemoryConsumption() const {
  UPDATE_WHEN_CLASS_CHANGES(WasmModule,
#if V8_ENABLE_DRUMBRAKE
                            952
#else   // V8_ENABLE_DRUMBRAKE
                            920
#endif  // V8_ENABLE_DRUMBRAKE
  );
  size_t result = EstimateStoredSize();

  result += type_feedback.EstimateCurrentMemoryConsumption();
  // For type_feedback.well_known_imports:
  result += num_imported_functions * sizeof(WellKnownImport);

  result += lazily_generated_names.EstimateCurrentMemoryConsumption();

  result += ContentSize(marked_for_tierup);
  result += ContentSize(feedback_slots_to_wire_byte_offsets);

  if (v8_flags.trace_wasm_offheap_memory) {
    PrintF("WasmModule: %zu\n", result);
  }
  return result;
}

size_t PrintSignature(base::Vector<char> buffer, const CanonicalSig* sig,
                      char delimiter) {
  if (buffer.empty()) return 0;
  size_t old_size = buffer.size();
  auto append_char = [&buffer](char c) {
    if (buffer.size() == 1) return;  // Keep last character for '\0'.
    buffer[0] = c;
    buffer += 1;
  };
  for (CanonicalValueType t : sig->parameters()) {
    append_char(t.short_name());
  }
  append_char(delimiter);
  for (CanonicalValueType t : sig->returns()) {
    append_char(t.short_name());
  }
  buffer[0] = '\0';
  return old_size - buffer.size();
}

int JumpTableOffset(const WasmModule* module, int func_index) {
  return JumpTableAssembler::JumpSlotIndexToOffset(
      declared_function_index(module, func_index));
}

size_t GetWireBytesHash(base::Vector<const uint8_t> wire_bytes) {
  return StringHasher::HashSequentialString(
      reinterpret_cast<const char*>(wire_bytes.begin()),
      static_cast<uint32_t>(wire_bytes.size()), HashSeed::Default());
}

int NumFeedbackSlots(const WasmModule* module, int func_index) {
  base::MutexGuard mutex_guard{&module->type_feedback.mutex};
  auto it = module->type_feedback.feedback_for_function.find(func_index);
  if (it == module->type_feedback.feedback_for_function.end()) {
    // The first slot is reserved for total invocation count.
    return FeedbackConstants::kHeaderSlots;
  }
  // The number of call instructions is capped by max function size.
  static_assert(kV8MaxWasmFunctionSize *
                        FeedbackConstants::kSlotsPerInstruction +
                    FeedbackConstants::kHeaderSlots <
                static_cast<size_t>(std::numeric_limits<int>::max()));
  // The first slot is reserved for total invocation count.
  return static_cast<int>(FeedbackConstants::kSlotsPerInstruction *
                              it->second.call_targets.size() +
                          FeedbackConstants::kHeaderSlots);
}

}  // namespace v8::internal::wasm