@ 261105.16
current
#include "module_wrap.h"

#include "debug_utils-inl.h"
#include "env.h"
#include "memory_tracker-inl.h"
#include "node_contextify.h"
#include "node_errors.h"
#include "node_external_reference.h"
#include "node_internals.h"
#include "node_process-inl.h"
#include "node_sea.h"
#include "node_url.h"
#include "node_watchdog.h"
#include "util-inl.h"

#include <sys/stat.h>  // S_IFDIR

#include <algorithm>

namespace node {
namespace loader {

using errors::TryCatchScope;

using node::contextify::ContextifyContext;
using v8::Array;
using v8::ArrayBufferView;
using v8::Boolean;
using v8::Context;
using v8::Data;
using v8::EscapableHandleScope;
using v8::Exception;
using v8::FixedArray;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Global;
using v8::HandleScope;
using v8::Int32;
using v8::Integer;
using v8::Isolate;
using v8::Just;
using v8::JustVoid;
using v8::Local;
using v8::LocalVector;
using v8::Maybe;
using v8::MaybeLocal;
using v8::MemorySpan;
using v8::Message;
using v8::MicrotaskQueue;
using v8::Module;
using v8::ModuleImportPhase;
using v8::ModuleRequest;
using v8::Name;
using v8::Nothing;
using v8::Null;
using v8::Object;
using v8::ObjectTemplate;
using v8::PrimitiveArray;
using v8::Promise;
using v8::PromiseRejectEvent;
using v8::PropertyAttribute;
using v8::PropertyCallbackInfo;
using v8::ScriptCompiler;
using v8::ScriptOrigin;
using v8::String;
using v8::Symbol;
using v8::UnboundModuleScript;
using v8::Undefined;
using v8::Value;

inline bool DataIsString(Local<Data> data) {
  return data->IsValue() && data.As<Value>()->IsString();
}

void ModuleCacheKey::MemoryInfo(MemoryTracker* tracker) const {
  tracker->TrackField("specifier", specifier);
  tracker->TrackField("import_attributes", import_attributes);
}

std::string ModuleCacheKey::ToString() const {
  std::string result = "ModuleCacheKey(\"" + specifier + "\"";
  if (!import_attributes.empty()) {
    result += ", {";
    bool first = true;
    for (const auto& attr : import_attributes) {
      if (first) {
        first = false;
      } else {
        result += ", ";
      }
      result += attr.first + ": " + attr.second;
    }
    result += "}";
  }
  result += ")";
  return result;
}

template <int elements_per_attribute>
ModuleCacheKey ModuleCacheKey::From(Local<String> specifier,
                                    Local<FixedArray> import_attributes) {
  CHECK_EQ(import_attributes->Length() % elements_per_attribute, 0);
  Isolate* isolate = Isolate::GetCurrent();
  std::size_t h1 = specifier->GetIdentityHash();
  size_t num_attributes = import_attributes->Length() / elements_per_attribute;
  ImportAttributeVector attributes;
  attributes.reserve(num_attributes);

  std::size_t h2 = 0;

  for (int i = 0; i < import_attributes->Length();
       i += elements_per_attribute) {
    DCHECK(DataIsString(import_attributes->Get(i)));
    DCHECK(DataIsString(import_attributes->Get(i + 1)));

    Local<String> v8_key = import_attributes->Get(i).As<String>();
    Local<String> v8_value = import_attributes->Get(i + 1).As<String>();
    Utf8Value key_utf8(isolate, v8_key);
    Utf8Value value_utf8(isolate, v8_value);

    attributes.emplace_back(key_utf8.ToString(), value_utf8.ToString());
    h2 ^= v8_key->GetIdentityHash();
    h2 ^= v8_value->GetIdentityHash();
  }

  // Combine the hashes using a simple XOR and bit shift to reduce
  // collisions. Note that the hash does not guarantee uniqueness.
  std::size_t hash = h1 ^ (h2 << 1);

  Utf8Value utf8_specifier(isolate, specifier);
  return ModuleCacheKey{utf8_specifier.ToString(), attributes, hash};
}

ModuleCacheKey ModuleCacheKey::From(Local<ModuleRequest> v8_request) {
  return From(v8_request->GetSpecifier(), v8_request->GetImportAttributes());
}

ModuleWrap::ModuleWrap(Realm* realm,
                       Local<Object> object,
                       Local<Module> module,
                       Local<String> url,
                       Local<Object> context_object,
                       Local<Value> synthetic_evaluation_step)
    : BaseObject(realm, object),
      url_(Utf8Value(realm->isolate(), url).ToString()),
      module_(realm->isolate(), module),
      module_hash_(module->GetIdentityHash()) {
  realm->env()->hash_to_module_map.emplace(module_hash_, this);

  object->SetInternalField(kModuleSlot, module);
  object->SetInternalField(kModuleSourceObjectSlot,
                           v8::Undefined(realm->isolate()));
  object->SetInternalField(kSyntheticEvaluationStepsSlot,
                           synthetic_evaluation_step);
  object->SetInternalField(kContextObjectSlot, context_object);
  object->SetInternalField(kLinkedRequestsSlot,
                           v8::Undefined(realm->isolate()));

  if (!synthetic_evaluation_step->IsUndefined()) {
    synthetic_ = true;
    // Synthetic modules have no dependencies.
    linked_ = true;
  }
  MakeWeak();
  module_.SetWeak();
}

ModuleWrap::~ModuleWrap() {
  auto range = env()->hash_to_module_map.equal_range(module_hash_);
  for (auto it = range.first; it != range.second; ++it) {
    if (it->second == this) {
      env()->hash_to_module_map.erase(it);
      break;
    }
  }
}

Local<Context> ModuleWrap::context() const {
  Local<Value> obj = object()->GetInternalField(kContextObjectSlot).As<Value>();
  // If this fails, there is likely a bug e.g. ModuleWrap::context() is accessed
  // before the ModuleWrap constructor completes.
  CHECK(obj->IsObject());
  return obj.As<Object>()->GetCreationContextChecked();
}

ModuleWrap* ModuleWrap::GetFromModule(Environment* env,
                                      Local<Module> module) {
  auto range = env->hash_to_module_map.equal_range(module->GetIdentityHash());
  for (auto it = range.first; it != range.second; ++it) {
    if (it->second->module_ == module) {
      return it->second;
    }
  }
  return nullptr;
}

Maybe<bool> ModuleWrap::CheckUnsettledTopLevelAwait() {
  Isolate* isolate = env()->isolate();
  Local<Context> context = env()->context();

  // This must be invoked when the environment is shutting down, and the module
  // is kept alive by the module wrap via an internal field.
  CHECK(env()->exiting());
  CHECK(!module_.IsEmpty());

  Local<Module> module = module_.Get(isolate);
  // It's a synthetic module, likely a facade wrapping CJS.
  if (!module->IsSourceTextModule()) {
    return Just(true);
  }

  if (!HasAsyncGraph()) {  // There is no TLA, no need to check.
    return Just(true);
  }

  auto stalled_messages =
      std::get<1>(module->GetStalledTopLevelAwaitMessages(isolate));
  if (stalled_messages.empty()) {
    return Just(true);
  }

  if (env()->options()->warnings) {
    for (auto& message : stalled_messages) {
      std::string reason = "Warning: Detected unsettled top-level await at ";
      std::string info =
          FormatErrorMessage(isolate, context, "", message, true);
      reason += info;
      FPrintF(stderr, "%s\n", reason);
    }
  }

  return Just(false);
}

bool ModuleWrap::HasAsyncGraph() {
  if (!has_async_graph_.has_value()) {
    Isolate* isolate = env()->isolate();
    HandleScope scope(isolate);

    has_async_graph_ = module_.Get(isolate)->IsGraphAsync();
  }
  return has_async_graph_.value();
}

Local<PrimitiveArray> ModuleWrap::GetHostDefinedOptions(
    Isolate* isolate, Local<Symbol> id_symbol) {
  Local<PrimitiveArray> host_defined_options =
      PrimitiveArray::New(isolate, HostDefinedOptions::kLength);
  host_defined_options->Set(isolate, HostDefinedOptions::kID, id_symbol);
  return host_defined_options;
}

// new ModuleWrap(url, context, source, lineOffset, columnOffset[, cachedData]);
// new ModuleWrap(url, context, source, lineOffset, columnOffset,
//                idSymbol);
// new ModuleWrap(url, context, exportNames, evaluationCallback[, cjsModule])
void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
  CHECK(args.IsConstructCall());
  CHECK_GE(args.Length(), 3);

  Realm* realm = Realm::GetCurrent(args);
  Isolate* isolate = realm->isolate();

  Local<Object> that = args.This();

  CHECK(args[0]->IsString());
  Local<String> url = args[0].As<String>();

  Local<Context> context;
  ContextifyContext* contextify_context = nullptr;
  if (args[1]->IsUndefined()) {
    context = that->GetCreationContextChecked();
  } else {
    CHECK(args[1]->IsObject());
    contextify_context = ContextifyContext::ContextFromContextifiedSandbox(
        realm->env(), args[1].As<Object>());
    CHECK_NOT_NULL(contextify_context);
    context = contextify_context->context();
  }

  int line_offset = 0;
  int column_offset = 0;

  bool synthetic = args[2]->IsArray();
  bool can_use_builtin_cache = false;
  Local<PrimitiveArray> host_defined_options =
      PrimitiveArray::New(isolate, HostDefinedOptions::kLength);
  Local<Symbol> id_symbol;
  if (synthetic) {
    // new ModuleWrap(url, context, exportNames, evaluationCallback[,
    // cjsModule])
    CHECK(args[3]->IsFunction());
  } else {
    // new ModuleWrap(url, context, source, lineOffset, columnOffset[,
    //                cachedData]);
    // new ModuleWrap(url, context, source, lineOffset, columnOffset,
    //                idSymbol);
    CHECK(args[2]->IsString());
    CHECK(args[3]->IsNumber());
    line_offset = args[3].As<Int32>()->Value();
    CHECK(args[4]->IsNumber());
    column_offset = args[4].As<Int32>()->Value();
    if (args[5]->IsSymbol()) {
      id_symbol = args[5].As<Symbol>();
      can_use_builtin_cache =
          (id_symbol ==
           realm->isolate_data()->source_text_module_default_hdo());
    } else {
      id_symbol = Symbol::New(isolate, url);
    }
    host_defined_options = GetHostDefinedOptions(isolate, id_symbol);

    if (that->SetPrivate(context,
                         realm->isolate_data()->host_defined_option_symbol(),
                         id_symbol)
            .IsNothing()) {
      return;
    }
  }

  ShouldNotAbortOnUncaughtScope no_abort_scope(realm->env());
  TryCatchScope try_catch(realm->env());

  Local<Module> module;

  {
    Context::Scope context_scope(context);
    if (synthetic) {
      CHECK(args[2]->IsArray());
      Local<Array> export_names_arr = args[2].As<Array>();

      uint32_t len = export_names_arr->Length();
      LocalVector<String> export_names(realm->isolate(), len);
      for (uint32_t i = 0; i < len; i++) {
        Local<Value> export_name_val;
        if (!export_names_arr->Get(context, i).ToLocal(&export_name_val)) {
          return;
        }
        CHECK(export_name_val->IsString());
        export_names[i] = export_name_val.As<String>();
      }

      const MemorySpan<const Local<String>> span(export_names.begin(),
                                                 export_names.size());
      module = Module::CreateSyntheticModule(
          isolate, url, span, SyntheticModuleEvaluationStepsCallback);
    } else {
      // When we are compiling for the default loader, this will be
      // std::nullopt, and CompileSourceTextModule() should use
      // on-disk cache.
      std::optional<ScriptCompiler::CachedData*> user_cached_data;
      if (id_symbol !=
          realm->isolate_data()->source_text_module_default_hdo()) {
        user_cached_data = nullptr;
      }
      if (args[5]->IsArrayBufferView()) {
        CHECK(!can_use_builtin_cache);  // We don't use this option internally.
        Local<ArrayBufferView> cached_data_buf = args[5].As<ArrayBufferView>();
        uint8_t* data =
            static_cast<uint8_t*>(cached_data_buf->Buffer()->Data());
        user_cached_data =
            new ScriptCompiler::CachedData(data + cached_data_buf->ByteOffset(),
                                           cached_data_buf->ByteLength());
      }
#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
      // For embedder ESM in a SEA, use the bundled code cache if available.
      if (id_symbol == realm->isolate_data()->embedder_module_hdo() &&
          sea::IsSingleExecutable()) {
        const sea::SeaResource& sea = sea::FindSingleExecutableResource();
        if (sea.use_code_cache()) {
          std::string_view data = sea.code_cache.value();
          user_cached_data = new ScriptCompiler::CachedData(
              reinterpret_cast<const uint8_t*>(data.data()),
              static_cast<int>(data.size()),
              ScriptCompiler::CachedData::BufferNotOwned);
        }
      }
#endif  // !DISABLE_SINGLE_EXECUTABLE_APPLICATION
      Local<String> source_text = args[2].As<String>();

      bool cache_rejected = false;
      if (!CompileSourceTextModule(realm,
                                   source_text,
                                   url,
                                   line_offset,
                                   column_offset,
                                   host_defined_options,
                                   user_cached_data,
                                   &cache_rejected)
               .ToLocal(&module)) {
        if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
          CHECK(!try_catch.Message().IsEmpty());
          CHECK(!try_catch.Exception().IsEmpty());
          AppendExceptionLine(realm->env(),
                              try_catch.Exception(),
                              try_catch.Message(),
                              ErrorHandlingMode::MODULE_ERROR);
          try_catch.ReThrow();
        }
        return;
      }

      if (user_cached_data.has_value() && user_cached_data.value() != nullptr) {
#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
        if (id_symbol == realm->isolate_data()->embedder_module_hdo() &&
            sea::IsSingleExecutable()) {
          if (cache_rejected) {
            per_process::Debug(DebugCategory::SEA,
                               "SEA module code cache rejected\n");
            ProcessEmitWarningSync(realm->env(), "Code cache data rejected.");
          } else {
            per_process::Debug(DebugCategory::SEA,
                               "SEA module code cache accepted\n");
          }
        } else  // NOLINT(readability/braces)
#endif          // !DISABLE_SINGLE_EXECUTABLE_APPLICATION
          if (cache_rejected) {
            THROW_ERR_VM_MODULE_CACHED_DATA_REJECTED(
                realm, "cachedData buffer was rejected");
            try_catch.ReThrow();
            return;
          }
      }

      if (that->Set(context,
                    realm->env()->has_top_level_await_string(),
                    Boolean::New(isolate, module->HasTopLevelAwait()))
              .IsNothing()) {
        return;
      }

      if (that->Set(context,
                    realm->env()->source_url_string(),
                    module->GetUnboundModuleScript()->GetSourceURL())
              .IsNothing()) {
        return;
      }

      if (that->Set(context,
                    realm->env()->source_map_url_string(),
                    module->GetUnboundModuleScript()->GetSourceMappingURL())
              .IsNothing()) {
        return;
      }
    }
  }

  if (that->Set(context,
                realm->isolate_data()->synthetic_string(),
                Boolean::New(isolate, synthetic))
          .IsNothing()) {
    return;
  }

  if (!that->Set(context, realm->isolate_data()->url_string(), url)
           .FromMaybe(false)) {
    return;
  }

  if (synthetic && args[4]->IsObject() &&
      that->Set(context, realm->isolate_data()->imported_cjs_symbol(), args[4])
          .IsNothing()) {
    return;
  }

  // Initialize an empty slot for source map cache before the object is frozen.
  if (that->SetPrivate(context,
                       realm->isolate_data()->source_map_data_private_symbol(),
                       Undefined(isolate))
          .IsNothing()) {
    return;
  }

  // Use the extras object as an object whose GetCreationContext() will be the
  // original `context`, since the `Context` itself strictly speaking cannot
  // be stored in an internal field.
  Local<Object> context_object = context->GetExtrasBindingObject();
  Local<Value> synthetic_evaluation_step =
      synthetic ? args[3] : Undefined(realm->isolate()).As<Value>();

  ModuleWrap* obj = new ModuleWrap(
      realm, that, module, url, context_object, synthetic_evaluation_step);

  obj->contextify_context_ = contextify_context;

  args.GetReturnValue().Set(that);
}

MaybeLocal<Module> ModuleWrap::CompileSourceTextModule(
    Realm* realm,
    Local<String> source_text,
    Local<String> url,
    int line_offset,
    int column_offset,
    Local<PrimitiveArray> host_defined_options,
    std::optional<ScriptCompiler::CachedData*> user_cached_data,
    bool* cache_rejected) {
  Isolate* isolate = realm->isolate();
  EscapableHandleScope scope(isolate);
  ScriptOrigin origin(url,
                      line_offset,
                      column_offset,
                      true,            // is cross origin
                      -1,              // script id
                      Local<Value>(),  // source map URL
                      false,           // is opaque (?)
                      false,           // is WASM
                      true,            // is ES Module
                      host_defined_options);
  ScriptCompiler::CachedData* cached_data = nullptr;
  CompileCacheEntry* cache_entry = nullptr;
  // When compiling for the default loader, user_cached_data is std::nullptr.
  // When compiling for vm.Module, it's either nullptr or a pointer to the
  // cached data.
  if (user_cached_data.has_value()) {
    cached_data = user_cached_data.value();
  } else if (realm->env()->use_compile_cache()) {
    cache_entry = realm->env()->compile_cache_handler()->GetOrInsert(
        source_text, url, CachedCodeType::kESM);
  }

  if (cache_entry != nullptr && cache_entry->cache != nullptr) {
    // source will take ownership of cached_data.
    cached_data = cache_entry->CopyCache();
  }

  ScriptCompiler::Source source(source_text, origin, cached_data);
  ScriptCompiler::CompileOptions options;
  if (cached_data == nullptr) {
    options = ScriptCompiler::kNoCompileOptions;
  } else {
    options = ScriptCompiler::kConsumeCodeCache;
  }

  Local<Module> module;
  if (!ScriptCompiler::CompileModule(isolate, &source, options)
           .ToLocal(&module)) {
    return scope.EscapeMaybe(MaybeLocal<Module>());
  }

  if (options == ScriptCompiler::kConsumeCodeCache) {
    *cache_rejected = source.GetCachedData()->rejected;
  }

  if (cache_entry != nullptr) {
    realm->env()->compile_cache_handler()->MaybeSave(
        cache_entry, module, *cache_rejected);
  }

  return scope.Escape(module);
}

ModulePhase to_phase_constant(ModuleImportPhase phase) {
  switch (phase) {
    case ModuleImportPhase::kEvaluation:
      return kEvaluationPhase;
    case ModuleImportPhase::kDefer:
      return kDeferPhase;
    case ModuleImportPhase::kSource:
      return kSourcePhase;
    default:
      UNREACHABLE();
  }
}

static Local<Object> createImportAttributesContainer(
    Realm* realm,
    Isolate* isolate,
    Local<FixedArray> raw_attributes,
    const int elements_per_attribute) {
  CHECK_EQ(raw_attributes->Length() % elements_per_attribute, 0);
  size_t num_attributes = raw_attributes->Length() / elements_per_attribute;
  LocalVector<Name> names(isolate, num_attributes);
  LocalVector<Value> values(isolate, num_attributes);

  for (int i = 0; i < raw_attributes->Length(); i += elements_per_attribute) {
    Local<Data> key = raw_attributes->Get(i);
    Local<Data> value = raw_attributes->Get(i + 1);
    DCHECK(DataIsString(key));
    DCHECK(DataIsString(value));

    int idx = i / elements_per_attribute;
    names[idx] = key.As<String>();
    values[idx] = value.As<String>();
  }

  Local<Object> attributes = Object::New(
      isolate, Null(isolate), names.data(), values.data(), num_attributes);
  attributes->SetIntegrityLevel(realm->context(), v8::IntegrityLevel::kFrozen)
      .Check();
  return attributes;
}

static Local<Array> createModuleRequestsContainer(
    Realm* realm, Isolate* isolate, Local<FixedArray> raw_requests) {
  EscapableHandleScope scope(isolate);
  Local<Context> context = realm->context();
  LocalVector<Value> requests(isolate, raw_requests->Length());

  for (int i = 0; i < raw_requests->Length(); i++) {
    DCHECK(raw_requests->Get(i)->IsModuleRequest());
    Local<ModuleRequest> module_request =
        raw_requests->Get(i).As<ModuleRequest>();

    Local<String> specifier = module_request->GetSpecifier();

    // Contains the import attributes for this request in the form:
    // [key1, value1, source_offset1, key2, value2, source_offset2, ...].
    Local<FixedArray> raw_attributes = module_request->GetImportAttributes();
    Local<Object> attributes =
        createImportAttributesContainer(realm, isolate, raw_attributes, 3);
    ModuleImportPhase phase = module_request->GetPhase();

    Local<Name> names[] = {
        realm->isolate_data()->specifier_string(),
        realm->isolate_data()->attributes_string(),
        realm->isolate_data()->phase_string(),
    };
    Local<Value> values[] = {
        specifier,
        attributes,
        Integer::New(isolate, to_phase_constant(phase)),
    };
    DCHECK_EQ(arraysize(names), arraysize(values));

    Local<Object> request =
        Object::New(isolate, Null(isolate), names, values, arraysize(names));
    request->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen).Check();

    requests[i] = request;
  }

  return scope.Escape(Array::New(isolate, requests.data(), requests.size()));
}

void ModuleWrap::GetModuleRequests(const FunctionCallbackInfo<Value>& args) {
  Realm* realm = Realm::GetCurrent(args);
  Isolate* isolate = args.GetIsolate();
  Local<Object> that = args.This();

  ModuleWrap* obj;
  ASSIGN_OR_RETURN_UNWRAP(&obj, that);

  Local<Module> module = obj->module_.Get(isolate);
  args.GetReturnValue().Set(createModuleRequestsContainer(
      realm, isolate, module->GetModuleRequests()));
}

// moduleWrap.link(moduleWraps)
void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  HandleScope handle_scope(isolate);
  Realm* realm = Realm::GetCurrent(args);
  Local<Context> context = realm->context();

  ModuleWrap* dependent;
  ASSIGN_OR_RETURN_UNWRAP(&dependent, args.This());

  CHECK_EQ(args.Length(), 1);

  Local<FixedArray> requests =
      dependent->module_.Get(isolate)->GetModuleRequests();
  Local<Array> modules = args[0].As<Array>();
  std::vector<Global<Value>> modules_vector;
  if (FromV8Array(context, modules, &modules_vector).IsEmpty()) {
    return;
  }
  size_t request_count = static_cast<size_t>(requests->Length());
  CHECK_EQ(modules_vector.size(), request_count);
  std::vector<ModuleWrap*> linked_module_wraps(request_count);

  // Track the duplicated module requests. For example if a modulelooks like
  // this:
  //
  // import { foo } from 'mod' with { type: 'json' };
  // import source ModSource from 'mod' with { type: 'json' };
  // import { baz } from 'mod2';
  //
  // The first two module requests are identical. The map would look like
  // { mod_key: 0, mod2_key: 2 } in this case, so that module request 0 and
  // module request 1 would be mapped to mod_key and both should resolve to the
  // module identified by module request 0 (the first one with this identity),
  // and module request 2 should resolve the module identified by index 2.
  std::unordered_map<ModuleCacheKey, size_t, ModuleCacheKey::Hash>
      module_request_map;

  for (size_t i = 0; i < request_count; i++) {
    // TODO(joyeecheung): merge this with the serializeKey() in module_map.js.
    // This currently doesn't sort the import attributes.
    Local<Value> module_value = modules_vector[i].Get(isolate);
    ModuleCacheKey module_cache_key =
        ModuleCacheKey::From(requests->Get(i).As<ModuleRequest>());
    auto it = module_request_map.find(module_cache_key);
    if (it == module_request_map.end()) {
      // This is the first request with this identity, record it - any mismatch
      // for this would only be found in subsequent requests, so no need to
      // check here.
      module_request_map[module_cache_key] = i;
    } else {  // This identity has been seen before, check for mismatch.
      size_t first_seen_index = it->second;
      // Check that the module is the same as the one resolved by the first
      // request with this identity.
      Local<Value> first_seen_value =
          modules_vector[first_seen_index].Get(isolate);
      if (!module_value->StrictEquals(first_seen_value)) {
        // If the module is different from the one of the same request, throw an
        // error.
        THROW_ERR_MODULE_LINK_MISMATCH(
            realm->env(),
            "Module request '%s' at index %d must be linked "
            "to the same module requested at index %d",
            module_cache_key.ToString(),
            i,
            first_seen_index);
        return;
      }
    }

    CHECK(module_value->IsObject());  // Guaranteed by link methods in JS land.
    ModuleWrap* resolved =
        BaseObject::Unwrap<ModuleWrap>(module_value.As<Object>());
    CHECK_NOT_NULL(resolved);  // Guaranteed by link methods in JS land.
    linked_module_wraps[i] = resolved;
  }

  args.This()->SetInternalField(kLinkedRequestsSlot, modules);
  std::swap(dependent->linked_module_wraps_, linked_module_wraps);
  dependent->linked_ = true;
}

void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) {
  Realm* realm = Realm::GetCurrent(args);
  Isolate* isolate = args.GetIsolate();
  ModuleWrap* obj;
  ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
  Local<Context> context = obj->context();
  Local<Module> module = obj->module_.Get(isolate);
  Environment* env = realm->env();

  if (!obj->IsLinked()) {
    THROW_ERR_VM_MODULE_LINK_FAILURE(env, "module is not linked");
    return;
  }

  {
    TryCatchScope try_catch(env);
    USE(module->InstantiateModule(
        context, ResolveModuleCallback, ResolveSourceCallback));

    if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
      CHECK(!try_catch.Message().IsEmpty());
      CHECK(!try_catch.Exception().IsEmpty());
      AppendExceptionLine(env,
                          try_catch.Exception(),
                          try_catch.Message(),
                          ErrorHandlingMode::MODULE_ERROR);
      try_catch.ReThrow();
      return;
    }
  }
}

void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
  Realm* realm = Realm::GetCurrent(args);
  Isolate* isolate = realm->isolate();
  ModuleWrap* obj;
  ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
  Local<Context> context = obj->context();
  Local<Module> module = obj->module_.Get(isolate);

  ContextifyContext* contextify_context = obj->contextify_context_;
  MicrotaskQueue* microtask_queue = nullptr;
  if (contextify_context != nullptr)
      microtask_queue = contextify_context->microtask_queue();

  // module.evaluate(timeout, breakOnSigint)
  CHECK_EQ(args.Length(), 2);

  CHECK(args[0]->IsNumber());
  int64_t timeout;
  if (!args[0]->IntegerValue(realm->context()).To(&timeout)) {
    return;
  }

  CHECK(args[1]->IsBoolean());
  bool break_on_sigint = args[1]->IsTrue();

  ShouldNotAbortOnUncaughtScope no_abort_scope(realm->env());
  TryCatchScope try_catch(realm->env());

  bool timed_out = false;
  bool received_signal = false;
  MaybeLocal<Value> result;
  {
    auto wd = timeout != -1
                  ? std::make_optional<Watchdog>(isolate, timeout, &timed_out)
                  : std::nullopt;
    auto swd = break_on_sigint ? std::make_optional<SigintWatchdog>(
                                     isolate, &received_signal)
                               : std::nullopt;

    result = module->Evaluate(context);

    Local<Value> res;
    if (result.ToLocal(&res) && microtask_queue) {
      DCHECK(res->IsPromise());

      // To address https://github.com/nodejs/node/issues/59541 when the
      // module has its own separate microtask queue in microtaskMode
      // "afterEvaluate", we avoid returning a promise built inside the
      // module's own context.
      //
      // Instead, we build a promise in the outer context, which we resolve
      // with {result}, then we checkpoint the module's own queue, and finally
      // we return the outer-context promise.
      //
      // If we simply returned the inner promise {result} directly, per
      // https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob, the outer
      // context, when resolving a promise coming from a different context,
      // would need to enqueue a task (known as a thenable job task) onto the
      // queue of that different context (the module's context). But this queue
      // will normally not be checkpointed after evaluate() returns.
      //
      // This means that the execution flow in the outer context would
      // silently fall through at the statement (in lib/internal/vm/module.js):
      //   await this[kWrap].evaluate(timeout, breakOnSigint)
      //
      // This is true for any promises created inside the module's context
      // and made available to the outer context, as the node:vm doc explains.
      //
      // We must handle this particular return value differently to make it
      // possible to await on the result of evaluate().
      Local<Context> outer_context = isolate->GetCurrentContext();
      Local<Promise::Resolver> resolver;
      if (!Promise::Resolver::New(outer_context).ToLocal(&resolver)) {
        result = {};
      }
      if (resolver->Resolve(outer_context, res).IsNothing()) {
        result = {};
      }
      result = resolver->GetPromise();

      microtask_queue->PerformCheckpoint(isolate);
    }
  }

  if (result.IsEmpty()) {
    CHECK(try_catch.HasCaught());
  }

  // Convert the termination exception into a regular exception.
  if (timed_out || received_signal) {
    if (!realm->env()->is_main_thread() && realm->env()->is_stopping()) return;
    isolate->CancelTerminateExecution();
    // It is possible that execution was terminated by another timeout in
    // which this timeout is nested, so check whether one of the watchdogs
    // from this invocation is responsible for termination.
    if (timed_out) {
      THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(realm->env(), timeout);
    } else if (received_signal) {
      THROW_ERR_SCRIPT_EXECUTION_INTERRUPTED(realm->env());
    }
  }

  if (try_catch.HasCaught()) {
    if (!try_catch.HasTerminated())
      try_catch.ReThrow();
    return;
  }

  Local<Value> res;
  if (result.ToLocal(&res)) {
    args.GetReturnValue().Set(res);
  }
}

Maybe<void> ThrowIfPromiseRejected(Realm* realm, Local<Promise> promise) {
  Isolate* isolate = realm->isolate();
  Local<Context> context = realm->context();
  if (promise->State() != Promise::PromiseState::kRejected) {
    return JustVoid();
  }
  // The rejected promise is created by V8, so we don't get a chance to mark
  // it as resolved before the rejection happens from evaluation. But we can
  // tell the promise rejection callback to treat it as a promise rejected
  // before handler was added which would remove it from the unhandled
  // rejection handling, since we are converting it into an error and throw
  // from here directly.
  Local<Value> type =
      Integer::New(isolate,
                   static_cast<int32_t>(
                       PromiseRejectEvent::kPromiseHandlerAddedAfterReject));
  Local<Value> args[] = {type, promise, Undefined(isolate)};
  if (realm->promise_reject_callback()
          ->Call(context, Undefined(isolate), arraysize(args), args)
          .IsEmpty()) {
    return Nothing<void>();
  }
  Local<Value> exception = promise->Result();
  Local<Message> message = Exception::CreateMessage(isolate, exception);
  AppendExceptionLine(
      realm->env(), exception, message, ErrorHandlingMode::MODULE_ERROR);
  isolate->ThrowException(exception);
  return Nothing<void>();
}

void ThrowIfPromiseRejected(const FunctionCallbackInfo<Value>& args) {
  if (!args[0]->IsPromise()) {
    return;
  }
  ThrowIfPromiseRejected(Realm::GetCurrent(args), args[0].As<Promise>());
}

void ModuleWrap::EvaluateSync(const FunctionCallbackInfo<Value>& args) {
  Realm* realm = Realm::GetCurrent(args);
  Isolate* isolate = args.GetIsolate();
  ModuleWrap* obj;
  ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
  Local<Context> context = obj->context();
  Local<Module> module = obj->module_.Get(isolate);
  Environment* env = realm->env();

  Local<Value> result;
  {
    TryCatchScope try_catch(env);
    if (!module->Evaluate(context).ToLocal(&result)) {
      if (try_catch.HasCaught()) {
        if (!try_catch.HasTerminated()) {
          try_catch.ReThrow();
        }
        return;
      }
    }
  }

  CHECK(result->IsPromise());
  Local<Promise> promise = result.As<Promise>();
  if (ThrowIfPromiseRejected(realm, promise).IsNothing()) {
    return;
  }

  // Graphs with top-level await are rejected by the caller before evaluation
  // starts, so the promise must have been settled synchronously.
  CHECK(!obj->HasAsyncGraph());
  CHECK_EQ(promise->State(), Promise::PromiseState::kFulfilled);

  args.GetReturnValue().Set(module->GetModuleNamespace());
}

void ModuleWrap::GetNamespace(const FunctionCallbackInfo<Value>& args) {
  Realm* realm = Realm::GetCurrent(args);
  Isolate* isolate = args.GetIsolate();
  ModuleWrap* obj;
  ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());

  Local<Module> module = obj->module_.Get(isolate);
  if (module->GetStatus() < Module::kInstantiated) {
    return THROW_ERR_MODULE_NOT_INSTANTIATED(realm->env());
  }

  Local<Value> result = module->GetModuleNamespace();
  args.GetReturnValue().Set(result);
}

void ModuleWrap::SetModuleSourceObject(
    const FunctionCallbackInfo<Value>& args) {
  ModuleWrap* obj;
  ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());

  CHECK_EQ(args.Length(), 1);
  CHECK(args[0]->IsObject());
  CHECK(obj->object()
            ->GetInternalField(kModuleSourceObjectSlot)
            .As<Value>()
            ->IsUndefined());

  obj->object()->SetInternalField(kModuleSourceObjectSlot, args[0]);
}

void ModuleWrap::GetModuleSourceObject(
    const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  ModuleWrap* obj;
  ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());

  CHECK_EQ(args.Length(), 0);
  Local<Value> module_source_object =
      obj->object()->GetInternalField(kModuleSourceObjectSlot).As<Value>();

  if (module_source_object->IsUndefined()) {
    THROW_ERR_SOURCE_PHASE_NOT_DEFINED(isolate, obj->url_);
    return;
  }

  args.GetReturnValue().Set(module_source_object);
}

void ModuleWrap::GetStatus(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  ModuleWrap* obj;
  ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());

  Local<Module> module = obj->module_.Get(isolate);

  args.GetReturnValue().Set(module->GetStatus());
}

void ModuleWrap::GetError(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  ModuleWrap* obj;
  ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());

  Local<Module> module = obj->module_.Get(isolate);
  args.GetReturnValue().Set(module->GetException());
}

void ModuleWrap::HasAsyncGraph(Local<Name> property,
                               const PropertyCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  Environment* env = Environment::GetCurrent(isolate);
  ModuleWrap* obj;
  ASSIGN_OR_RETURN_UNWRAP(&obj, args.Holder());

  Local<Module> module = obj->module_.Get(isolate);
  if (module->GetStatus() < Module::kInstantiated) {
    return THROW_ERR_MODULE_NOT_INSTANTIATED(env);
  }

  args.GetReturnValue().Set(obj->HasAsyncGraph());
}

// static
MaybeLocal<Module> ModuleWrap::ResolveModuleCallback(
    Local<Context> context,
    size_t module_request_index,
    Local<Module> referrer) {
  ModuleWrap* resolved_module;
  if (!ResolveModule(context, module_request_index, referrer)
           .To(&resolved_module)) {
    return {};
  }
  DCHECK_NOT_NULL(resolved_module);
  return resolved_module->module_.Get(Isolate::GetCurrent());
}

// static
MaybeLocal<Object> ModuleWrap::ResolveSourceCallback(
    Local<Context> context,
    size_t module_request_index,
    Local<Module> referrer) {
  ModuleWrap* resolved_module;
  if (!ResolveModule(context, module_request_index, referrer)
           .To(&resolved_module)) {
    return {};
  }
  DCHECK_NOT_NULL(resolved_module);

  Local<Value> module_source_object =
      resolved_module->object()
          ->GetInternalField(ModuleWrap::kModuleSourceObjectSlot)
          .As<Value>();
  if (module_source_object->IsUndefined()) {
    THROW_ERR_SOURCE_PHASE_NOT_DEFINED(Isolate::GetCurrent(),
                                       resolved_module->url_);
    return {};
  }
  CHECK(module_source_object->IsObject());
  return module_source_object.As<Object>();
}

static std::string GetSpecifierFromModuleRequest(Local<Module> referrer,
                                                 size_t module_request_index) {
  Local<ModuleRequest> raw_request =
      referrer->GetModuleRequests()
          ->Get(static_cast<int>(module_request_index))
          .As<ModuleRequest>();
  Local<String> specifier = raw_request->GetSpecifier();
  Utf8Value specifier_utf8(Isolate::GetCurrent(), specifier);
  return specifier_utf8.ToString();
}

// static
Maybe<ModuleWrap*> ModuleWrap::ResolveModule(Local<Context> context,
                                             size_t module_request_index,
                                             Local<Module> referrer) {
  Isolate* isolate = Isolate::GetCurrent();
  Environment* env = Environment::GetCurrent(context);
  if (env == nullptr) {
    THROW_ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE(isolate);
    return Nothing<ModuleWrap*>();
  }
  // Check that the referrer is not yet been instantiated.
  DCHECK(referrer->GetStatus() <= Module::kInstantiated);

  ModuleWrap* dependent = ModuleWrap::GetFromModule(env, referrer);
  if (dependent == nullptr) {
    std::string specifier =
        GetSpecifierFromModuleRequest(referrer, module_request_index);
    THROW_ERR_VM_MODULE_LINK_FAILURE(
        env, "request for '%s' is from invalid module", specifier);
    return Nothing<ModuleWrap*>();
  }
  if (!dependent->IsLinked()) {
    std::string specifier =
        GetSpecifierFromModuleRequest(referrer, module_request_index);
    THROW_ERR_VM_MODULE_LINK_FAILURE(env,
                                     "request for '%s' can not be resolved on "
                                     "module '%s' that is not linked",
                                     specifier,
                                     dependent->url_);
    return Nothing<ModuleWrap*>();
  }

  size_t linked_module_count = dependent->linked_module_wraps_.size();
  if (linked_module_count > 0) {
    CHECK_LT(module_request_index, linked_module_count);
  } else {
    UNREACHABLE("Module resolution callback invoked for a module"
                " without linked requests");
  }

  return Just(dependent->linked_module_wraps_[module_request_index]);
}

static MaybeLocal<Promise> ImportModuleDynamicallyWithPhase(
    Local<Context> context,
    Local<Data> host_defined_options,
    Local<Value> resource_name,
    Local<String> specifier,
    ModuleImportPhase phase,
    Local<FixedArray> import_attributes) {
  Isolate* isolate = Isolate::GetCurrent();
  Environment* env = Environment::GetCurrent(context);
  if (env == nullptr) {
    THROW_ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE(isolate);
    return MaybeLocal<Promise>();
  }
  Realm* realm = Realm::GetCurrent(context);
  if (realm == nullptr) {
    // Fallback to the principal realm if it's in a vm context.
    realm = env->principal_realm();
  }

  EscapableHandleScope handle_scope(isolate);

  Local<Function> import_callback =
      realm->host_import_module_dynamically_callback();
  Local<Value> id;

  Local<FixedArray> options = host_defined_options.As<FixedArray>();
  // Get referrer id symbol from the host-defined options.
  // If the host-defined options are empty, get the referrer id symbol
  // from the realm global object.
  if (options->Length() == HostDefinedOptions::kLength) {
    id = options->Get(HostDefinedOptions::kID).As<Symbol>();
  } else if (!context->Global()
                  ->GetPrivate(context, env->host_defined_option_symbol())
                  .ToLocal(&id)) {
    return MaybeLocal<Promise>();
  }

  Local<Object> attributes =
      createImportAttributesContainer(realm, isolate, import_attributes, 2);

  Local<Value> import_args[] = {
      id,
      Local<Value>(specifier),
      Integer::New(isolate, to_phase_constant(phase)),
      attributes,
      resource_name,
  };

  Local<Value> result;
  if (!import_callback
           ->Call(
               context, Undefined(isolate), arraysize(import_args), import_args)
           .ToLocal(&result)) {
    return {};
  }

  // Wrap the returned value in a promise created in the referrer context to
  // avoid dynamic scopes.
  Local<Promise::Resolver> resolver;
  if (!Promise::Resolver::New(context).ToLocal(&resolver)) {
    return {};
  }
  if (resolver->Resolve(context, result).IsNothing()) {
    return {};
  }
  return handle_scope.Escape(resolver->GetPromise());
}

static MaybeLocal<Promise> ImportModuleDynamically(
    Local<Context> context,
    Local<Data> host_defined_options,
    Local<Value> resource_name,
    Local<String> specifier,
    Local<FixedArray> import_attributes) {
  return ImportModuleDynamicallyWithPhase(context,
                                          host_defined_options,
                                          resource_name,
                                          specifier,
                                          ModuleImportPhase::kEvaluation,
                                          import_attributes);
}

void ModuleWrap::SetImportModuleDynamicallyCallback(
    const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  Realm* realm = Realm::GetCurrent(args);
  HandleScope handle_scope(isolate);

  CHECK_EQ(args.Length(), 1);
  CHECK(args[0]->IsFunction());
  Local<Function> import_callback = args[0].As<Function>();
  realm->set_host_import_module_dynamically_callback(import_callback);

  isolate->SetHostImportModuleDynamicallyCallback(ImportModuleDynamically);
  isolate->SetHostImportModuleWithPhaseDynamicallyCallback(
      ImportModuleDynamicallyWithPhase);
}

void ModuleWrap::SetImportMetaResolveInitializer(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();
  Realm* realm = Realm::GetCurrent(args);
  HandleScope handle_scope(isolate);

  CHECK_EQ(args.Length(), 1);
  CHECK(args[0]->IsFunction());
  Local<Function> initializer = args[0].As<Function>();
  realm->set_host_import_meta_resolve_initializer(initializer);
}

static void ImportMetaResolveLazyGetter(
    Local<v8::Name> name, const PropertyCallbackInfo<Value>& info) {
  Isolate* isolate = info.GetIsolate();
  Local<Value> receiver_val = info.Holder();
  if (!receiver_val->IsObject()) {
    THROW_ERR_INVALID_INVOCATION(isolate);
    return;
  }
  Local<Object> receiver = receiver_val.As<Object>();
  Local<Context> context;
  if (!receiver->GetCreationContext().ToLocal(&context)) {
    THROW_ERR_INVALID_INVOCATION(isolate);
    return;
  }
  Realm* realm = Realm::GetCurrent(context);
  if (realm == nullptr) {
    THROW_ERR_INVALID_INVOCATION(isolate);
  }
  Local<Function> initializer = realm->host_import_meta_resolve_initializer();
  if (initializer.IsEmpty()) {
    THROW_ERR_INVALID_INVOCATION(isolate);
    return;
  }

  // This should be createImportMetaResolve(). The loader argument is already
  // bound at initialization time.
  Local<Value> args[] = {info.Data()};
  Local<Value> ret;
  if (!initializer
           ->Call(context, Undefined(realm->isolate()), arraysize(args), args)
           .ToLocal(&ret)) {
    return;
  }
  info.GetReturnValue().Set(ret);
}

static void PathHelpersLazyGetter(Local<v8::Name> name,
                                  const PropertyCallbackInfo<Value>& info) {
  Isolate* isolate = info.GetIsolate();
  // This getter has no JavaScript function representation and is not
  // invoked in the creation context.
  // When this getter is invoked in a vm context, the `Realm::GetCurrent(info)`
  // returns a nullptr and retrieve the creation context via `this` object and
  // get the creation Realm.
  Local<Value> receiver_val = info.Holder();
  if (!receiver_val->IsObject()) {
    THROW_ERR_INVALID_INVOCATION(isolate);
    return;
  }
  Local<Object> receiver = receiver_val.As<Object>();
  Local<Context> context;
  if (!receiver->GetCreationContext().ToLocal(&context)) {
    THROW_ERR_INVALID_INVOCATION(isolate);
    return;
  }
  Environment* env = Environment::GetCurrent(context);

  node::Utf8Value url(isolate, info.Data());
  auto file_url = ada::parse(url.ToStringView());
  CHECK(file_url);
  auto file_path = url::FileURLToPath(env, *file_url);
  CHECK(file_path.has_value());
  std::string_view ret_view = file_path.value();

  node::Utf8Value utf8name(isolate, name);
  auto plain_name = utf8name.ToStringView();
  if (plain_name == "dirname") {
#ifdef _WIN32
#define PATH_SEPARATOR '\\'
#else
#define PATH_SEPARATOR '/'
#endif
    auto index = ret_view.rfind(PATH_SEPARATOR);
    CHECK(index != std::string_view::npos);
    ret_view.remove_suffix(ret_view.size() - index);
#undef PATH_SEPARATOR
  }
  Local<Value> ret;
  if (!ToV8Value(context, ret_view, isolate).ToLocal(&ret)) {
    return;
  }
  info.GetReturnValue().Set(ret);
}

static Maybe<void> DefaultImportMetaObjectInitializer(Realm* realm,
                                                      Local<Object> wrap,
                                                      Local<Object> meta) {
  Local<Context> context = realm->context();
  Isolate* isolate = realm->isolate();
  Environment* env = realm->env();

  Local<Value> url;
  if (!wrap->Get(context, env->url_string()).ToLocal(&url)) {
    return Nothing<void>();
  }

  // N.B.: Order is important to keep keys in alphabetical order.

  Utf8Value url_utf8(isolate, url);
  if (url_utf8.ToStringView().starts_with("file:")) {
    // Set a lazy getter of import.meta.dirname
    if (meta->SetLazyDataProperty(
                context, env->dirname_string(), PathHelpersLazyGetter, url)
            .IsNothing()) {
      return Nothing<void>();
    }

    // Set a lazy getter of import.meta.filename
    if (meta->SetLazyDataProperty(
                context, env->filename_string(), PathHelpersLazyGetter, url)
            .IsNothing()) {
      return Nothing<void>();
    }
  }

  // Set import.meta.main = moduleWrap.isMain
  Local<Value> is_main;
  if (!wrap->Get(context, FIXED_ONE_BYTE_STRING(isolate, "isMain"))
           .ToLocal(&is_main)) {
    return Nothing<void>();
  }
  if (meta->Set(context,
                FIXED_ONE_BYTE_STRING(isolate, "main"),
                Boolean::New(isolate, is_main->IsTrue()))
          .IsEmpty()) {
    return Nothing<void>();
  }

  // Set a lazy getter of import.meta.resolve - only if the initializer is set,
  // which is only the case when run on a non-loader-hook thread.
  Local<Function> import_meta_resolve_initializer =
      realm->host_import_meta_resolve_initializer();
  if (!import_meta_resolve_initializer.IsEmpty() &&
      meta->SetLazyDataProperty(context,
                                FIXED_ONE_BYTE_STRING(isolate, "resolve"),
                                ImportMetaResolveLazyGetter,
                                url)
          .IsNothing()) {
    return Nothing<void>();
  }

  // Set import.meta.url = moduleWrap.url
  if (meta->Set(context, env->url_string(), url).IsEmpty()) {
    return Nothing<void>();
  }

  return JustVoid();
}

void ModuleWrap::HostInitializeImportMetaObjectCallback(
    Local<Context> context, Local<Module> module, Local<Object> meta) {
  Environment* env = Environment::GetCurrent(context);
  if (env == nullptr)
    return;
  ModuleWrap* module_wrap = GetFromModule(env, module);

  if (module_wrap == nullptr) {
    return;
  }
  Realm* realm = Realm::GetCurrent(context);
  if (realm == nullptr) {
    // Fallback to the principal realm if it's in a vm context.
    realm = env->principal_realm();
  }

  Local<Object> wrap = module_wrap->object();
  Local<Function> callback =
      realm->host_initialize_import_meta_object_callback();
  Local<Value> id;
  if (!wrap->GetPrivate(context, env->host_defined_option_symbol())
           .ToLocal(&id)) {
    return;
  }
  DCHECK(id->IsSymbol());

  // Use the default initializer for source text modules without custom
  // callbacks.
  if (id == env->source_text_module_default_hdo() ||
      id == env->embedder_module_hdo()) {
    USE(DefaultImportMetaObjectInitializer(realm, wrap, meta));
    return;
  }

  // For modules that have custom callbacks, call into JS land
  // to look up the callback from the registry.
  Local<Value> args[] = {id, meta, wrap};
  TryCatchScope try_catch(env);

  USE(callback->Call(
      context, Undefined(realm->isolate()), arraysize(args), args));
  if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
    try_catch.ReThrow();
  }
}

void ModuleWrap::SetInitializeImportMetaObjectCallback(
    const FunctionCallbackInfo<Value>& args) {
  Realm* realm = Realm::GetCurrent(args);
  Isolate* isolate = realm->isolate();

  CHECK_EQ(args.Length(), 1);
  CHECK(args[0]->IsFunction());
  Local<Function> import_meta_callback = args[0].As<Function>();
  realm->set_host_initialize_import_meta_object_callback(import_meta_callback);

  isolate->SetHostInitializeImportMetaObjectCallback(
      HostInitializeImportMetaObjectCallback);
}

MaybeLocal<Value> ModuleWrap::SyntheticModuleEvaluationStepsCallback(
    Local<Context> context, Local<Module> module) {
  Environment* env = Environment::GetCurrent(context);
  Isolate* isolate = env->isolate();

  ModuleWrap* obj = GetFromModule(env, module);

  TryCatchScope try_catch(env);
  Local<Function> synthetic_evaluation_steps =
      obj->object()
          ->GetInternalField(kSyntheticEvaluationStepsSlot)
          .As<Value>()
          .As<Function>();
  obj->object()->SetInternalField(
      kSyntheticEvaluationStepsSlot, Undefined(isolate));
  MaybeLocal<Value> ret = synthetic_evaluation_steps->Call(context,
      obj->object(), 0, nullptr);
  if (ret.IsEmpty()) {
    CHECK(try_catch.HasCaught());
  }
  if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
    CHECK(!try_catch.Message().IsEmpty());
    CHECK(!try_catch.Exception().IsEmpty());
    try_catch.ReThrow();
    return MaybeLocal<Value>();
  }

  Local<Promise::Resolver> resolver;
  if (!Promise::Resolver::New(context).ToLocal(&resolver)) {
    return MaybeLocal<Value>();
  }

  if (resolver->Resolve(context, Undefined(isolate)).IsNothing()) {
    return MaybeLocal<Value>();
  }
  return resolver->GetPromise();
}

void ModuleWrap::SetSyntheticExport(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  Local<Object> that = args.This();

  ModuleWrap* obj;
  ASSIGN_OR_RETURN_UNWRAP(&obj, that);

  CHECK(obj->synthetic_);

  CHECK_EQ(args.Length(), 2);

  CHECK(args[0]->IsString());
  Local<String> export_name = args[0].As<String>();

  Local<Value> export_value = args[1];

  Local<Module> module = obj->module_.Get(isolate);
  USE(module->SetSyntheticModuleExport(isolate, export_name, export_value));
}

void ModuleWrap::CreateCachedData(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  Local<Object> that = args.This();

  ModuleWrap* obj;
  ASSIGN_OR_RETURN_UNWRAP(&obj, that);

  CHECK(!obj->synthetic_);

  Local<Module> module = obj->module_.Get(isolate);

  CHECK_LT(module->GetStatus(), Module::Status::kEvaluating);

  Local<UnboundModuleScript> unbound_module_script =
      module->GetUnboundModuleScript();
  std::unique_ptr<ScriptCompiler::CachedData> cached_data(
      ScriptCompiler::CreateCodeCache(unbound_module_script));
  Environment* env = Environment::GetCurrent(args);
  Local<Object> result;
  if (!cached_data) {
    if (!Buffer::New(env, 0).ToLocal(&result)) {
      return;
    }
  } else if (!Buffer::Copy(env,
                           reinterpret_cast<const char*>(cached_data->data),
                           cached_data->length)
                  .ToLocal(&result)) {
    return;
  }
  args.GetReturnValue().Set(result);
}

// This v8::Module::ResolveModuleCallback simply links `import 'original'`
// to the env->temporary_required_module_facade_original() which is stashed
// right before this callback is called and will be restored as soon as
// v8::Module::Instantiate() returns.
MaybeLocal<Module> LinkRequireFacadeWithOriginal(
    Local<Context> context,
    Local<String> specifier,
    Local<FixedArray> import_attributes,
    Local<Module> referrer) {
  Environment* env = Environment::GetCurrent(context);
  Isolate* isolate = Isolate::GetCurrent();
  CHECK(specifier->Equals(context, env->original_string()).ToChecked());
  CHECK(!env->temporary_required_module_facade_original.IsEmpty());
  return env->temporary_required_module_facade_original.Get(isolate);
}

// Wraps an existing source text module with a facade that adds
// .__esModule = true to the exports.
// See env->required_module_facade_source_string() for the source.
void ModuleWrap::CreateRequiredModuleFacade(
    const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  Local<Context> context = isolate->GetCurrentContext();
  Environment* env = Environment::GetCurrent(context);
  CHECK(args[0]->IsObject());  // original module
  Local<Object> wrap = args[0].As<Object>();
  ModuleWrap* original;
  ASSIGN_OR_RETURN_UNWRAP(&original, wrap);

  // Use the same facade source and URL to hit the compilation cache.
  ScriptOrigin origin(env->required_module_facade_url_string(),
                      0,               // line offset
                      0,               // column offset
                      true,            // is cross origin
                      -1,              // script id
                      Local<Value>(),  // source map URL
                      false,           // is opaque (?)
                      false,           // is WASM
                      true);           // is ES Module
  ScriptCompiler::Source source(env->required_module_facade_source_string(),
                                origin);

  // The module facade instantiation simply links `import 'original'` in the
  // facade with the original module and should never fail.
  Local<Module> facade;
  if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&facade)) {
    return;
  }
  // Stash the original module in temporary_required_module_facade_original
  // for the LinkRequireFacadeWithOriginal() callback to pick it up.
  CHECK(env->temporary_required_module_facade_original.IsEmpty());
  env->temporary_required_module_facade_original.Reset(
      isolate, original->module_.Get(isolate));
  CHECK(facade->InstantiateModule(context, LinkRequireFacadeWithOriginal)
            .IsJust());
  env->temporary_required_module_facade_original.Reset();

  // The evaluation of the facade is synchronous.
  Local<Value> evaluated;
  if (!facade->Evaluate(context).ToLocal(&evaluated)) {
    return;
  }
  CHECK(evaluated->IsPromise());
  CHECK_EQ(evaluated.As<Promise>()->State(), Promise::PromiseState::kFulfilled);

  args.GetReturnValue().Set(facade->GetModuleNamespace());
}

void ModuleWrap::CreatePerIsolateProperties(IsolateData* isolate_data,
                                            Local<ObjectTemplate> target) {
  Isolate* isolate = isolate_data->isolate();

  Local<FunctionTemplate> tpl = NewFunctionTemplate(isolate, New);
  tpl->InstanceTemplate()->SetInternalFieldCount(
      ModuleWrap::kInternalFieldCount);

  SetProtoMethod(isolate, tpl, "link", Link);
  SetProtoMethod(isolate, tpl, "getModuleRequests", GetModuleRequests);
  SetProtoMethod(isolate, tpl, "instantiate", Instantiate);
  SetProtoMethod(isolate, tpl, "evaluateSync", EvaluateSync);
  SetProtoMethod(isolate, tpl, "evaluate", Evaluate);
  SetProtoMethod(isolate, tpl, "setExport", SetSyntheticExport);
  SetProtoMethod(isolate, tpl, "setModuleSourceObject", SetModuleSourceObject);
  SetProtoMethod(isolate, tpl, "getModuleSourceObject", GetModuleSourceObject);
  SetProtoMethodNoSideEffect(
      isolate, tpl, "createCachedData", CreateCachedData);
  SetProtoMethodNoSideEffect(isolate, tpl, "getNamespace", GetNamespace);
  SetProtoMethodNoSideEffect(isolate, tpl, "getStatus", GetStatus);
  SetProtoMethodNoSideEffect(isolate, tpl, "getError", GetError);
  SetConstructorFunction(isolate, target, "ModuleWrap", tpl);

  tpl->InstanceTemplate()->SetLazyDataProperty(
      FIXED_ONE_BYTE_STRING(isolate, "hasAsyncGraph"),
      HasAsyncGraph,
      Local<Value>(),
      PropertyAttribute::DontEnum);

  isolate_data->set_module_wrap_constructor_template(tpl);

  SetMethod(isolate,
            target,
            "setImportModuleDynamicallyCallback",
            SetImportModuleDynamicallyCallback);
  SetMethod(isolate,
            target,
            "setInitializeImportMetaObjectCallback",
            SetInitializeImportMetaObjectCallback);
  SetMethod(isolate,
            target,
            "setImportMetaResolveInitializer",
            SetImportMetaResolveInitializer);
  SetMethod(isolate,
            target,
            "createRequiredModuleFacade",
            CreateRequiredModuleFacade);
  SetMethod(isolate, target, "throwIfPromiseRejected", ThrowIfPromiseRejected);
}

void ModuleWrap::CreatePerContextProperties(Local<Object> target,
                                            Local<Value> unused,
                                            Local<Context> context,
                                            void* priv) {
  Realm* realm = Realm::GetCurrent(context);
  Isolate* isolate = realm->isolate();
#define V(enum_type, name)                                                     \
  target                                                                       \
      ->Set(context,                                                           \
            FIXED_ONE_BYTE_STRING(isolate, #name),                             \
            Integer::New(isolate, enum_type::name))                            \
      .FromJust()
  V(Module::Status, kUninstantiated);
  V(Module::Status, kInstantiating);
  V(Module::Status, kInstantiated);
  V(Module::Status, kEvaluating);
  V(Module::Status, kEvaluated);
  V(Module::Status, kErrored);

  V(ModulePhase, kEvaluationPhase);
  V(ModulePhase, kDeferPhase);
  V(ModulePhase, kSourcePhase);
#undef V
}

void ModuleWrap::RegisterExternalReferences(
    ExternalReferenceRegistry* registry) {
  registry->Register(New);

  registry->Register(Link);
  registry->Register(GetModuleRequests);
  registry->Register(EvaluateSync);
  registry->Register(Instantiate);
  registry->Register(Evaluate);
  registry->Register(SetSyntheticExport);
  registry->Register(SetModuleSourceObject);
  registry->Register(GetModuleSourceObject);
  registry->Register(CreateCachedData);
  registry->Register(GetNamespace);
  registry->Register(GetStatus);
  registry->Register(GetError);
  registry->Register(HasAsyncGraph);

  registry->Register(CreateRequiredModuleFacade);

  registry->Register(SetImportModuleDynamicallyCallback);
  registry->Register(SetInitializeImportMetaObjectCallback);
  registry->Register(SetImportMetaResolveInitializer);
  registry->Register(ThrowIfPromiseRejected);
}
}  // namespace loader
}  // namespace node

NODE_BINDING_CONTEXT_AWARE_INTERNAL(
    module_wrap, node::loader::ModuleWrap::CreatePerContextProperties)
NODE_BINDING_PER_ISOLATE_INIT(
    module_wrap, node::loader::ModuleWrap::CreatePerIsolateProperties)
NODE_BINDING_EXTERNAL_REFERENCE(
    module_wrap, node::loader::ModuleWrap::RegisterExternalReferences)