tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Modules.cpp (111158B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /* JavaScript modules (as in, the syntactic construct) implementation. */
      8 
      9 #include "vm/Modules.h"
     10 
     11 #include "mozilla/Assertions.h"  // MOZ_ASSERT
     12 #include "mozilla/ScopeExit.h"
     13 #include "mozilla/Utf8.h"  // mozilla::Utf8Unit
     14 
     15 #include <stdint.h>  // uint32_t
     16 
     17 #include "jstypes.h"  // JS_PUBLIC_API
     18 
     19 #include "builtin/JSON.h"  // js::ParseJSONWithReviver
     20 #include "builtin/ModuleObject.h"
     21 #include "builtin/Promise.h"  // js::CreatePromiseObjectForAsync, js::AsyncFunctionReturned
     22 #include "ds/Sort.h"
     23 #include "frontend/BytecodeCompiler.h"  // js::frontend::CompileModule
     24 #include "frontend/FrontendContext.h"   // js::AutoReportFrontendContext
     25 #include "js/ColumnNumber.h"            // JS::ColumnNumberOneOrigin
     26 #include "js/Context.h"                 // js::AssertHeapIsIdle
     27 #include "js/ErrorReport.h"             // JSErrorBase
     28 #include "js/friend/StackLimits.h"      // js::AutoCheckRecursionLimit
     29 #include "js/RootingAPI.h"              // JS::MutableHandle
     30 #include "js/Value.h"                   // JS::Value
     31 #include "vm/EnvironmentObject.h"       // js::ModuleEnvironmentObject
     32 #include "vm/JSAtomUtils.h"             // AtomizeString
     33 #include "vm/JSContext.h"               // CHECK_THREAD, JSContext
     34 #include "vm/JSObject.h"                // JSObject
     35 #include "vm/JSONParser.h"              // JSONParser
     36 #include "vm/List.h"                    // ListObject
     37 #include "vm/Runtime.h"                 // JSRuntime
     38 
     39 #include "builtin/HandlerFunction-inl.h"  // js::ExtraValueFromHandler, js::NewHandler{,WithExtraValue}, js::TargetFromHandler
     40 #include "vm/JSAtomUtils-inl.h"           // AtomToId
     41 #include "vm/JSContext-inl.h"             // JSContext::{c,releaseC}heck
     42 #include "vm/JSObject-inl.h"
     43 #include "vm/NativeObject-inl.h"
     44 
     45 using namespace js;
     46 
     47 using mozilla::Utf8Unit;
     48 
     49 class DynamicImportContextObject;
     50 
     51 static bool ModuleLink(JSContext* cx, Handle<ModuleObject*> module);
     52 static bool ModuleEvaluate(JSContext* cx, Handle<ModuleObject*> module,
     53                           MutableHandle<Value> rval);
     54 static bool SyntheticModuleEvaluate(JSContext* cx, Handle<ModuleObject*> module,
     55                                    MutableHandle<Value> rval);
     56 static bool ContinueModuleLoading(JSContext* cx,
     57                                  Handle<GraphLoadingStateRecordObject*> state,
     58                                  Handle<ModuleObject*> moduleCompletion,
     59                                  ImportPhase phase, Handle<Value> error);
     60 static bool TryStartDynamicModuleImport(JSContext* cx, HandleScript script,
     61                                        HandleValue specifierArg,
     62                                        HandleValue optionsArg,
     63                                        HandleObject promise);
     64 static bool ContinueDynamicImport(JSContext* cx, Handle<JSScript*> referrer,
     65                                  Handle<PromiseObject*> promiseCapability,
     66                                  Handle<ModuleObject*> module,
     67                                  ImportPhase phase, bool usePromise);
     68 static bool LinkAndEvaluateDynamicImport(JSContext* cx, unsigned argc,
     69                                         Value* vp);
     70 static bool LinkAndEvaluateDynamicImport(
     71    JSContext* cx, Handle<DynamicImportContextObject*> context);
     72 static bool DynamicImportResolved(JSContext* cx, unsigned argc, Value* vp);
     73 static bool DynamicImportRejected(JSContext* cx, unsigned argc, Value* vp);
     74 
     75 ////////////////////////////////////////////////////////////////////////////////
     76 // Public API
     77 
     78 JS_PUBLIC_API JS::ModuleLoadHook JS::GetModuleLoadHook(JSRuntime* rt) {
     79  AssertHeapIsIdle();
     80 
     81  return rt->moduleLoadHook;
     82 }
     83 
     84 JS_PUBLIC_API void JS::SetModuleLoadHook(JSRuntime* rt, ModuleLoadHook func) {
     85  AssertHeapIsIdle();
     86 
     87  rt->moduleLoadHook = func;
     88 }
     89 
     90 JS_PUBLIC_API JS::ModuleMetadataHook JS::GetModuleMetadataHook(JSRuntime* rt) {
     91  AssertHeapIsIdle();
     92 
     93  return rt->moduleMetadataHook;
     94 }
     95 
     96 JS_PUBLIC_API void JS::SetModuleMetadataHook(JSRuntime* rt,
     97                                             ModuleMetadataHook func) {
     98  AssertHeapIsIdle();
     99 
    100  rt->moduleMetadataHook = func;
    101 }
    102 
    103 // https://tc39.es/ecma262/#sec-FinishLoadingImportedModule
    104 JS_PUBLIC_API bool JS::FinishLoadingImportedModule(
    105    JSContext* cx, Handle<JSScript*> referrer, Handle<JSObject*> moduleRequest,
    106    Handle<Value> payload, Handle<JSObject*> result, bool usePromise) {
    107  AssertHeapIsIdle();
    108  CHECK_THREAD(cx);
    109  cx->check(referrer, moduleRequest, payload, result);
    110 
    111  MOZ_ASSERT(moduleRequest->is<ModuleRequestObject>());
    112  MOZ_ASSERT(result);
    113  Rooted<ModuleObject*> module(cx, &result->as<ModuleObject>());
    114 
    115  if (referrer && referrer->isModule()) {
    116    // |loadedModules| is only required to be stored on modules.
    117 
    118    // Step 1. If result is a normal completion, then
    119    // Step 1.a. If referrer.[[LoadedModules]] contains a Record whose
    120    //           [[Specifier]] is specifier, then
    121    LoadedModuleMap& loadedModules = referrer->module()->loadedModules();
    122    if (auto record = loadedModules.lookup(moduleRequest)) {
    123      //  Step 1.a.i. Assert: That Record's [[Module]] is result.[[Value]].
    124      MOZ_ASSERT(record->value() == module);
    125    } else {
    126      // Step 1.b. Else, append the Record { moduleRequest.[[Specifer]],
    127      //           [[Attributes]]: moduleRequest.[[Attributes]],
    128      //           [[Module]]: result.[[Value]] } to referrer.[[LoadedModules]].
    129      if (!loadedModules.putNew(moduleRequest, module)) {
    130        ReportOutOfMemory(cx);
    131        return FinishLoadingImportedModuleFailedWithPendingException(cx,
    132                                                                     payload);
    133      }
    134    }
    135  }
    136 
    137  // Step 2. If payload is a GraphLoadingState Record, then
    138  // Step 2.a. Perform ContinueModuleLoading(payload, result).
    139  JSObject* object = &payload.toObject();
    140  if (object->is<GraphLoadingStateRecordObject>()) {
    141    Rooted<GraphLoadingStateRecordObject*> state(cx);
    142    state = &object->as<GraphLoadingStateRecordObject>();
    143    return ContinueModuleLoading(
    144        cx, state, module, moduleRequest->as<ModuleRequestObject>().phase(),
    145        UndefinedHandleValue);
    146  }
    147 
    148  // Step 3. Else,
    149  // Step 3.a. Perform ContinueDynamicImport(payload, result).
    150  MOZ_ASSERT(object->is<PromiseObject>());
    151  Rooted<PromiseObject*> promise(cx, &object->as<PromiseObject>());
    152  return ContinueDynamicImport(cx, referrer, promise, module,
    153                               moduleRequest->as<ModuleRequestObject>().phase(),
    154                               usePromise);
    155 }
    156 
    157 // https://tc39.es/ecma262/#sec-FinishLoadingImportedModule
    158 // Failure path where |result| is a throw completion, supplied as |error|.
    159 JS_PUBLIC_API bool JS::FinishLoadingImportedModuleFailed(
    160    JSContext* cx, Handle<Value> payloadArg, Handle<Value> error) {
    161  AssertHeapIsIdle();
    162  CHECK_THREAD(cx);
    163  cx->check(payloadArg, error);
    164  MOZ_ASSERT(!JS_IsExceptionPending(cx));
    165 
    166  // Step 2. If payload is a GraphLoadingState Record, then
    167  // Step 2.a. Perform ContinueModuleLoading(payload, result).
    168  JSObject* payload = &payloadArg.toObject();
    169  if (payload->is<GraphLoadingStateRecordObject>()) {
    170    Rooted<GraphLoadingStateRecordObject*> state(cx);
    171    state = &payload->as<GraphLoadingStateRecordObject>();
    172    return ContinueModuleLoading(cx, state, nullptr, ImportPhase::Evaluation,
    173                                 error);
    174  }
    175 
    176  // Step 3. Else,
    177  // Step 3.a. Perform ContinueDynamicImport(payload, result).
    178  // ContinueDynamicImport:
    179  // Step 1. If moduleCompletion is an abrupt completion, then
    180  // Step 1. a. Perform ! Call(promiseCapability.[[Reject]], undefined,
    181  //            moduleCompletion.[[Value]]).
    182  Rooted<PromiseObject*> promise(cx, &payload->as<PromiseObject>());
    183  return PromiseObject::reject(cx, promise, error);
    184 }
    185 
    186 // https://tc39.es/ecma262/#sec-FinishLoadingImportedModule
    187 // Failure path where |result| is a throw completion, set as the pending
    188 // exception.
    189 JS_PUBLIC_API bool JS::FinishLoadingImportedModuleFailedWithPendingException(
    190    JSContext* cx, Handle<Value> payload) {
    191  AssertHeapIsIdle();
    192  CHECK_THREAD(cx);
    193  cx->check(payload);
    194  MOZ_ASSERT(JS_IsExceptionPending(cx));
    195 
    196  RootedValue error(cx);
    197  if (!cx->getPendingException(&error)) {
    198    MOZ_ASSERT(cx->isThrowingOutOfMemory());
    199    MOZ_ALWAYS_TRUE(cx->getPendingException(&error));
    200  }
    201  cx->clearPendingException();
    202 
    203  return FinishLoadingImportedModuleFailed(cx, payload, error);
    204 }
    205 
    206 template <typename Unit>
    207 static JSObject* CompileModuleHelper(JSContext* cx,
    208                                     const JS::ReadOnlyCompileOptions& options,
    209                                     JS::SourceText<Unit>& srcBuf) {
    210  MOZ_ASSERT(!cx->zone()->isAtomsZone());
    211  AssertHeapIsIdle();
    212  CHECK_THREAD(cx);
    213 
    214  JS::Rooted<JSObject*> mod(cx);
    215  {
    216    AutoReportFrontendContext fc(cx);
    217    mod = frontend::CompileModule(cx, &fc, options, srcBuf);
    218  }
    219  return mod;
    220 }
    221 
    222 JS_PUBLIC_API JSObject* JS::CompileModule(JSContext* cx,
    223                                          const ReadOnlyCompileOptions& options,
    224                                          SourceText<char16_t>& srcBuf) {
    225  return CompileModuleHelper(cx, options, srcBuf);
    226 }
    227 
    228 JS_PUBLIC_API JSObject* JS::CompileModule(JSContext* cx,
    229                                          const ReadOnlyCompileOptions& options,
    230                                          SourceText<Utf8Unit>& srcBuf) {
    231  return CompileModuleHelper(cx, options, srcBuf);
    232 }
    233 
    234 JS_PUBLIC_API JSObject* JS::CompileJsonModule(
    235    JSContext* cx, const ReadOnlyCompileOptions& options,
    236    SourceText<mozilla::Utf8Unit>& srcBuf) {
    237  size_t length = srcBuf.length();
    238  auto chars =
    239      UniqueTwoByteChars(UTF8CharsToNewTwoByteCharsZ(
    240                             cx, JS::UTF8Chars(srcBuf.get(), srcBuf.length()),
    241                             &length, js::MallocArena)
    242                             .get());
    243  if (!chars) {
    244    return nullptr;
    245  }
    246 
    247  JS::SourceText<char16_t> source;
    248  if (!source.init(cx, std::move(chars), length)) {
    249    return nullptr;
    250  }
    251 
    252  return CompileJsonModule(cx, options, source);
    253 }
    254 
    255 JS_PUBLIC_API JSObject* JS::CompileJsonModule(
    256    JSContext* cx, const ReadOnlyCompileOptions& options,
    257    SourceText<char16_t>& srcBuf) {
    258  MOZ_ASSERT(!cx->zone()->isAtomsZone());
    259  AssertHeapIsIdle();
    260  CHECK_THREAD(cx);
    261 
    262  auto charRange =
    263      mozilla::Range<const char16_t>(srcBuf.get(), srcBuf.length());
    264  Rooted<JSONParser<char16_t>> parser(
    265      cx, cx, charRange, JSONParser<char16_t>::ParseType::JSONParse);
    266 
    267  parser.reportLineNumbersFromParsedData(true);
    268  parser.setFilename(options.filename());
    269 
    270  JS::RootedValue jsonValue(cx);
    271  if (!parser.parse(&jsonValue)) {
    272    return nullptr;
    273  }
    274 
    275  return CreateDefaultExportSyntheticModule(cx, jsonValue);
    276 }
    277 
    278 JS_PUBLIC_API JSObject* JS::CreateDefaultExportSyntheticModule(
    279    JSContext* cx, const Value& defaultExport) {
    280  Rooted<ExportNameVector> exportNames(cx);
    281  if (!exportNames.append(cx->names().default_)) {
    282    ReportOutOfMemory(cx);
    283    return nullptr;
    284  }
    285 
    286  Rooted<ModuleObject*> moduleObject(
    287      cx, ModuleObject::createSynthetic(cx, &exportNames));
    288  if (!moduleObject) {
    289    return nullptr;
    290  }
    291 
    292  RootedVector<Value> exportValues(cx);
    293  if (!exportValues.append(defaultExport)) {
    294    ReportOutOfMemory(cx);
    295    return nullptr;
    296  }
    297 
    298  if (!ModuleObject::createSyntheticEnvironment(cx, moduleObject,
    299                                                exportValues)) {
    300    return nullptr;
    301  }
    302 
    303  return moduleObject;
    304 }
    305 
    306 JS_PUBLIC_API JSObject* JS::CompileWasmModule(
    307    JSContext* cx, const ReadOnlyCompileOptions& options,
    308    SourceText<mozilla::Utf8Unit>& srcBuf) {
    309  // TODO: Compilation of wasm modules will be added in
    310  // https://bugzilla.mozilla.org/show_bug.cgi?id=1997621.
    311  // For now, we fail unconditionally.
    312  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    313                           JSMSG_WASM_COMPILE_ERROR,
    314                           "Compilation of wasm modules not implemented.");
    315 
    316  return nullptr;
    317 }
    318 
    319 JS_PUBLIC_API JSObject* JS::CompileWasmModule(
    320    JSContext* cx, const ReadOnlyCompileOptions& options,
    321    SourceText<char16_t>& srcBuf) {
    322  // TODO: Compilation of wasm modules will be added in
    323  // https://bugzilla.mozilla.org/show_bug.cgi?id=1997621.
    324  // For now, we fail unconditionally.
    325  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    326                           JSMSG_WASM_COMPILE_ERROR,
    327                           "Compilation of wasm modules not implemented.");
    328  return nullptr;
    329 }
    330 
    331 JS_PUBLIC_API void JS::SetModulePrivate(JSObject* module, const Value& value) {
    332  JSRuntime* rt = module->zone()->runtimeFromMainThread();
    333  module->as<ModuleObject>().scriptSourceObject()->setPrivate(rt, value);
    334 }
    335 
    336 JS_PUBLIC_API void JS::ClearModulePrivate(JSObject* module) {
    337  // |module| may be gray, be careful not to create edges to it.
    338  JSRuntime* rt = module->zone()->runtimeFromMainThread();
    339  module->as<ModuleObject>().scriptSourceObject()->clearPrivate(rt);
    340 }
    341 
    342 JS_PUBLIC_API JS::Value JS::GetModulePrivate(JSObject* module) {
    343  return module->as<ModuleObject>().scriptSourceObject()->getPrivate();
    344 }
    345 
    346 JS_PUBLIC_API bool JS::IsCyclicModule(JSObject* module) {
    347  return module->as<ModuleObject>().hasCyclicModuleFields();
    348 }
    349 
    350 JS_PUBLIC_API bool JS::ModuleLink(JSContext* cx, Handle<JSObject*> moduleArg) {
    351  AssertHeapIsIdle();
    352  CHECK_THREAD(cx);
    353  cx->releaseCheck(moduleArg);
    354 
    355  return ::ModuleLink(cx, moduleArg.as<ModuleObject>());
    356 }
    357 
    358 JS_PUBLIC_API bool JS::LoadRequestedModules(
    359    JSContext* cx, Handle<JSObject*> moduleArg, Handle<Value> hostDefined,
    360    JS::LoadModuleResolvedCallback resolved,
    361    JS::LoadModuleRejectedCallback rejected) {
    362  AssertHeapIsIdle();
    363  CHECK_THREAD(cx);
    364  cx->releaseCheck(moduleArg);
    365 
    366  return js::LoadRequestedModules(cx, moduleArg.as<ModuleObject>(), hostDefined,
    367                                  resolved, rejected);
    368 }
    369 
    370 JS_PUBLIC_API bool JS::LoadRequestedModules(
    371    JSContext* cx, Handle<JSObject*> moduleArg, Handle<Value> hostDefined,
    372    MutableHandle<JSObject*> promiseOut) {
    373  AssertHeapIsIdle();
    374  CHECK_THREAD(cx);
    375  cx->releaseCheck(moduleArg);
    376 
    377  return js::LoadRequestedModules(cx, moduleArg.as<ModuleObject>(), hostDefined,
    378                                  promiseOut);
    379 }
    380 
    381 JS_PUBLIC_API bool JS::ModuleEvaluate(JSContext* cx,
    382                                      Handle<JSObject*> moduleRecord,
    383                                      MutableHandle<JS::Value> rval) {
    384  AssertHeapIsIdle();
    385  CHECK_THREAD(cx);
    386  cx->releaseCheck(moduleRecord);
    387 
    388  cx->isEvaluatingModule++;
    389  auto guard = mozilla::MakeScopeExit([cx] {
    390    MOZ_ASSERT(cx->isEvaluatingModule != 0);
    391    cx->isEvaluatingModule--;
    392  });
    393 
    394  if (moduleRecord.as<ModuleObject>()->hasSyntheticModuleFields()) {
    395    return SyntheticModuleEvaluate(cx, moduleRecord.as<ModuleObject>(), rval);
    396  }
    397 
    398  return ::ModuleEvaluate(cx, moduleRecord.as<ModuleObject>(), rval);
    399 }
    400 
    401 JS_PUBLIC_API bool JS::ThrowOnModuleEvaluationFailure(
    402    JSContext* cx, Handle<JSObject*> evaluationPromise,
    403    ModuleErrorBehaviour errorBehaviour) {
    404  AssertHeapIsIdle();
    405  CHECK_THREAD(cx);
    406  cx->releaseCheck(evaluationPromise);
    407 
    408  return OnModuleEvaluationFailure(cx, evaluationPromise, errorBehaviour);
    409 }
    410 
    411 JS_PUBLIC_API JS::ModuleType JS::GetRequestedModuleType(
    412    JSContext* cx, Handle<JSObject*> moduleRecord, uint32_t index) {
    413  AssertHeapIsIdle();
    414  CHECK_THREAD(cx);
    415  cx->check(moduleRecord);
    416 
    417  auto& module = moduleRecord->as<ModuleObject>();
    418  return module.requestedModules()[index].moduleRequest()->moduleType();
    419 }
    420 
    421 JS_PUBLIC_API JSScript* JS::GetModuleScript(JS::HandleObject moduleRecord) {
    422  AssertHeapIsIdle();
    423 
    424  auto& module = moduleRecord->as<ModuleObject>();
    425 
    426  // A synthetic module does not have a script associated with it.
    427  if (module.hasSyntheticModuleFields()) {
    428    return nullptr;
    429  }
    430 
    431  return module.script();
    432 }
    433 
    434 JS_PUBLIC_API JSObject* JS::GetModuleObject(HandleScript moduleScript) {
    435  AssertHeapIsIdle();
    436  MOZ_ASSERT(moduleScript->isModule());
    437 
    438  return moduleScript->module();
    439 }
    440 
    441 JS_PUBLIC_API JSObject* JS::GetModuleNamespace(JSContext* cx,
    442                                               HandleObject moduleRecord) {
    443  AssertHeapIsIdle();
    444  CHECK_THREAD(cx);
    445  cx->check(moduleRecord);
    446  MOZ_ASSERT(moduleRecord->is<ModuleObject>());
    447 
    448  return GetOrCreateModuleNamespace(cx, moduleRecord.as<ModuleObject>());
    449 }
    450 
    451 JS_PUBLIC_API JSObject* JS::GetModuleForNamespace(
    452    JSContext* cx, HandleObject moduleNamespace) {
    453  AssertHeapIsIdle();
    454  CHECK_THREAD(cx);
    455  cx->check(moduleNamespace);
    456  MOZ_ASSERT(moduleNamespace->is<ModuleNamespaceObject>());
    457 
    458  return &moduleNamespace->as<ModuleNamespaceObject>().module();
    459 }
    460 
    461 JS_PUBLIC_API JSObject* JS::GetModuleEnvironment(JSContext* cx,
    462                                                 Handle<JSObject*> moduleObj) {
    463  AssertHeapIsIdle();
    464  CHECK_THREAD(cx);
    465  cx->check(moduleObj);
    466  MOZ_ASSERT(moduleObj->is<ModuleObject>());
    467 
    468  return moduleObj->as<ModuleObject>().environment();
    469 }
    470 
    471 JS_PUBLIC_API JSString* JS::GetModuleRequestSpecifier(
    472    JSContext* cx, Handle<JSObject*> moduleRequestArg) {
    473  AssertHeapIsIdle();
    474  CHECK_THREAD(cx);
    475  cx->check(moduleRequestArg);
    476 
    477  return moduleRequestArg->as<ModuleRequestObject>().specifier();
    478 }
    479 
    480 JS_PUBLIC_API JS::ModuleType JS::GetModuleRequestType(
    481    JSContext* cx, Handle<JSObject*> moduleRequestArg) {
    482  AssertHeapIsIdle();
    483  CHECK_THREAD(cx);
    484  cx->check(moduleRequestArg);
    485 
    486  return moduleRequestArg->as<ModuleRequestObject>().moduleType();
    487 }
    488 
    489 JS_PUBLIC_API void JS::ClearModuleEnvironment(JSObject* moduleObj) {
    490  MOZ_ASSERT(moduleObj);
    491  AssertHeapIsIdle();
    492 
    493  js::ModuleEnvironmentObject* env =
    494      moduleObj->as<js::ModuleObject>().environment();
    495  if (!env) {
    496    return;
    497  }
    498 
    499  const JSClass* clasp = env->getClass();
    500  uint32_t numReserved = JSCLASS_RESERVED_SLOTS(clasp);
    501  uint32_t numSlots = env->slotSpan();
    502  for (uint32_t i = numReserved; i < numSlots; i++) {
    503    env->setSlot(i, UndefinedValue());
    504  }
    505 }
    506 
    507 JS_PUBLIC_API bool JS::ModuleIsLinked(JSObject* moduleObj) {
    508  AssertHeapIsIdle();
    509  return moduleObj->as<ModuleObject>().status() != ModuleStatus::New &&
    510         moduleObj->as<ModuleObject>().status() != ModuleStatus::Unlinked;
    511 }
    512 
    513 ////////////////////////////////////////////////////////////////////////////////
    514 // Internal implementation
    515 
    516 class ResolveSetEntry {
    517  ModuleObject* module_;
    518  JSAtom* exportName_;
    519 
    520 public:
    521  ResolveSetEntry(ModuleObject* module, JSAtom* exportName)
    522      : module_(module), exportName_(exportName) {}
    523 
    524  ModuleObject* module() const { return module_; }
    525  JSAtom* exportName() const { return exportName_; }
    526 
    527  void trace(JSTracer* trc) {
    528    TraceRoot(trc, &module_, "ResolveSetEntry::module_");
    529    TraceRoot(trc, &exportName_, "ResolveSetEntry::exportName_");
    530  }
    531 };
    532 
    533 using ResolveSet = GCVector<ResolveSetEntry, 0, SystemAllocPolicy>;
    534 
    535 using ModuleSet =
    536    GCHashSet<ModuleObject*, DefaultHasher<ModuleObject*>, SystemAllocPolicy>;
    537 
    538 static bool CyclicModuleResolveExport(JSContext* cx,
    539                                      Handle<ModuleObject*> module,
    540                                      Handle<JSAtom*> exportName,
    541                                      MutableHandle<ResolveSet> resolveSet,
    542                                      MutableHandle<Value> result,
    543                                      ModuleErrorInfo* errorInfoOut = nullptr);
    544 static bool SyntheticModuleResolveExport(JSContext* cx,
    545                                         Handle<ModuleObject*> module,
    546                                         Handle<JSAtom*> exportName,
    547                                         MutableHandle<Value> result,
    548                                         ModuleErrorInfo* errorInfoOut);
    549 static ModuleNamespaceObject* ModuleNamespaceCreate(
    550    JSContext* cx, Handle<ModuleObject*> module,
    551    MutableHandle<UniquePtr<ExportNameVector>> exports);
    552 static bool InnerModuleLinking(JSContext* cx, Handle<ModuleObject*> module,
    553                               MutableHandle<ModuleVector> stack, size_t index,
    554                               size_t* indexOut);
    555 static bool InnerModuleEvaluation(JSContext* cx, Handle<ModuleObject*> module,
    556                                  MutableHandle<ModuleVector> stack,
    557                                  size_t index, size_t* indexOut);
    558 static bool ExecuteAsyncModule(JSContext* cx, Handle<ModuleObject*> module);
    559 static bool GatherAvailableModuleAncestors(
    560    JSContext* cx, Handle<ModuleObject*> module,
    561    MutableHandle<ModuleVector> execList);
    562 
    563 static const char* ModuleStatusName(ModuleStatus status) {
    564  switch (status) {
    565    case ModuleStatus::New:
    566      return "New";
    567    case ModuleStatus::Unlinked:
    568      return "Unlinked";
    569    case ModuleStatus::Linking:
    570      return "Linking";
    571    case ModuleStatus::Linked:
    572      return "Linked";
    573    case ModuleStatus::Evaluating:
    574      return "Evaluating";
    575    case ModuleStatus::EvaluatingAsync:
    576      return "EvaluatingAsync";
    577    case ModuleStatus::Evaluated:
    578      return "Evaluated";
    579    default:
    580      MOZ_CRASH("Unexpected ModuleStatus");
    581  }
    582 }
    583 
    584 static bool ContainsElement(const ExportNameVector& list, JSAtom* atom) {
    585  for (JSAtom* a : list) {
    586    if (a == atom) {
    587      return true;
    588    }
    589  }
    590 
    591  return false;
    592 }
    593 
    594 static bool ContainsElement(Handle<ModuleVector> stack, ModuleObject* module) {
    595  for (ModuleObject* m : stack) {
    596    if (m == module) {
    597      return true;
    598    }
    599  }
    600 
    601  return false;
    602 }
    603 
    604 #ifdef DEBUG
    605 static size_t CountElements(Handle<ModuleVector> stack, ModuleObject* module) {
    606  size_t count = 0;
    607  for (ModuleObject* m : stack) {
    608    if (m == module) {
    609      count++;
    610    }
    611  }
    612 
    613  return count;
    614 }
    615 #endif
    616 
    617 // https://tc39.es/proposal-json-modules/#sec-smr-getexportednames
    618 static bool SyntheticModuleGetExportedNames(
    619    JSContext* cx, Handle<ModuleObject*> module,
    620    MutableHandle<ExportNameVector> exportedNames) {
    621  MOZ_ASSERT(exportedNames.empty());
    622 
    623  if (!exportedNames.appendAll(module->syntheticExportNames())) {
    624    ReportOutOfMemory(cx);
    625    return false;
    626  }
    627 
    628  return true;
    629 }
    630 
    631 // https://tc39.es/ecma262/#sec-GetImportedModule
    632 static ModuleObject* GetImportedModule(
    633    JSContext* cx, Handle<ModuleObject*> referrer,
    634    Handle<ModuleRequestObject*> moduleRequest) {
    635  MOZ_ASSERT(referrer);
    636  MOZ_ASSERT(moduleRequest);
    637 
    638  // Step 1. Assert: Exactly one element of referrer.[[LoadedModules]] is a
    639  //         Record whose [[Specifier]] is specifier, since LoadRequestedModules
    640  //         has completed successfully on referrer prior to invoking this
    641  //         abstract operation.
    642  //
    643  //         Impl note: Updated to perform lookup using ModuleRequestObject as
    644  //         per the Import Attributes Proposal.
    645  auto record = referrer->loadedModules().lookup(moduleRequest);
    646  MOZ_ASSERT(record);
    647 
    648  // Step 2. Let record be the Record in referrer.[[LoadedModules]] whose
    649  //         [[Specifier]] is specifier.
    650  // Step 3. Return record.[[Module]].
    651  return record->value();
    652 }
    653 
    654 // https://tc39.es/ecma262/#sec-getexportednames
    655 // ES2023 16.2.1.6.2 GetExportedNames
    656 static bool ModuleGetExportedNames(
    657    JSContext* cx, Handle<ModuleObject*> module,
    658    MutableHandle<ModuleSet> exportStarSet,
    659    MutableHandle<ExportNameVector> exportedNames) {
    660  // Step 4. Let exportedNames be a new empty List.
    661  MOZ_ASSERT(exportedNames.empty());
    662 
    663  if (module->hasSyntheticModuleFields()) {
    664    return SyntheticModuleGetExportedNames(cx, module, exportedNames);
    665  }
    666 
    667  // Step 2. If exportStarSet contains module, then:
    668  if (exportStarSet.has(module)) {
    669    // Step 2.a. We've reached the starting point of an export * circularity.
    670    // Step 2.b. Return a new empty List.
    671    return true;
    672  }
    673 
    674  // Step 3. Append module to exportStarSet.
    675  if (!exportStarSet.put(module)) {
    676    ReportOutOfMemory(cx);
    677    return false;
    678  }
    679 
    680  // Step 5. For each ExportEntry Record e of module.[[LocalExportEntries]], do:
    681  for (const ExportEntry& e : module->localExportEntries()) {
    682    // Step 5.a. Assert: module provides the direct binding for this export.
    683    // Step 5.b. Append e.[[ExportName]] to exportedNames.
    684    if (!exportedNames.append(e.exportName())) {
    685      ReportOutOfMemory(cx);
    686      return false;
    687    }
    688  }
    689 
    690  // Step 6. For each ExportEntry Record e of module.[[IndirectExportEntries]],
    691  //         do:
    692  for (const ExportEntry& e : module->indirectExportEntries()) {
    693    // Step 6.a. Assert: module imports a specific binding for this export.
    694    // Step 6.b. Append e.[[ExportName]] to exportedNames.
    695    if (!exportedNames.append(e.exportName())) {
    696      ReportOutOfMemory(cx);
    697      return false;
    698    }
    699  }
    700 
    701  // Step 7. For each ExportEntry Record e of module.[[StarExportEntries]], do:
    702  Rooted<ModuleRequestObject*> moduleRequest(cx);
    703  Rooted<ModuleObject*> requestedModule(cx);
    704  Rooted<JSAtom*> name(cx);
    705  for (const ExportEntry& e : module->starExportEntries()) {
    706    // Step 7.a. Let requestedModule be ? GetImportedModule(module,
    707    //           e.[[ModuleRequest]]).
    708    moduleRequest = e.moduleRequest();
    709    requestedModule = GetImportedModule(cx, module, moduleRequest);
    710    if (!requestedModule) {
    711      return false;
    712    }
    713    MOZ_ASSERT(requestedModule->status() >= ModuleStatus::Unlinked);
    714 
    715    // Step 7.b. Let starNames be ?
    716    //           requestedModule.GetExportedNames(exportStarSet).
    717    Rooted<ExportNameVector> starNames(cx);
    718    if (!ModuleGetExportedNames(cx, requestedModule, exportStarSet,
    719                                &starNames)) {
    720      return false;
    721    }
    722 
    723    // Step 7.c. For each element n of starNames, do:
    724    for (JSAtom* name : starNames) {
    725      // Step 7.c.i. If SameValue(n, "default") is false, then:
    726      if (name != cx->names().default_) {
    727        // Step 7.c.i.1. If n is not an element of exportedNames, then:
    728        if (!ContainsElement(exportedNames, name)) {
    729          // Step 7.c.i.1.a. Append n to exportedNames.
    730          if (!exportedNames.append(name)) {
    731            ReportOutOfMemory(cx);
    732            return false;
    733          }
    734        }
    735      }
    736    }
    737  }
    738 
    739  // Step 8. Return exportedNames.
    740  return true;
    741 }
    742 
    743 static void ThrowUnexpectedModuleStatus(JSContext* cx, ModuleStatus status) {
    744  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    745                           JSMSG_BAD_MODULE_STATUS, ModuleStatusName(status));
    746 }
    747 
    748 // https://tc39.es/ecma262/#sec-HostLoadImportedModule
    749 //
    750 // According to spec the referrer can be a module script, classic script or
    751 // realm. The first two are supplied to this function by passing the script.
    752 // When the referrer is a realm nullptr is passed.
    753 bool js::HostLoadImportedModule(JSContext* cx, Handle<JSScript*> referrer,
    754                                Handle<JSObject*> moduleRequest,
    755                                Handle<Value> hostDefined,
    756                                Handle<Value> payload, uint32_t lineNumber,
    757                                JS::ColumnNumberOneOrigin columnNumber) {
    758  MOZ_ASSERT(moduleRequest);
    759  MOZ_ASSERT(!payload.isUndefined());
    760 
    761  JS::ModuleLoadHook moduleLoadHook = cx->runtime()->moduleLoadHook;
    762  if (!moduleLoadHook) {
    763    JS_ReportErrorASCII(cx, "Module load hook not set");
    764    return false;
    765  }
    766 
    767  bool ok = moduleLoadHook(cx, referrer, moduleRequest, hostDefined, payload,
    768                           lineNumber, columnNumber);
    769 
    770  if (!ok) {
    771    MOZ_ASSERT(JS_IsExceptionPending(cx));
    772    if (JS_IsExceptionPending(cx)) {
    773      return JS::FinishLoadingImportedModuleFailedWithPendingException(cx,
    774                                                                       payload);
    775    }
    776 
    777    return JS::FinishLoadingImportedModuleFailed(cx, payload,
    778                                                 UndefinedHandleValue);
    779  }
    780 
    781  return true;
    782 }
    783 
    784 static bool ModuleResolveExportImpl(JSContext* cx, Handle<ModuleObject*> module,
    785                                    Handle<JSAtom*> exportName,
    786                                    MutableHandle<ResolveSet> resolveSet,
    787                                    MutableHandle<Value> result,
    788                                    ModuleErrorInfo* errorInfoOut = nullptr) {
    789  if (module->hasSyntheticModuleFields()) {
    790    return SyntheticModuleResolveExport(cx, module, exportName, result,
    791                                        errorInfoOut);
    792  }
    793 
    794  return CyclicModuleResolveExport(cx, module, exportName, resolveSet, result,
    795                                   errorInfoOut);
    796 }
    797 
    798 // https://tc39.es/ecma262/#sec-resolveexport
    799 // ES2023 16.2.1.6.3 ResolveExport
    800 //
    801 // Returns an value describing the location of the resolved export or indicating
    802 // a failure.
    803 //
    804 // On success this returns a resolved binding record: { module, bindingName }
    805 //
    806 // There are two failure cases:
    807 //
    808 //  - If no definition was found or the request is found to be circular, *null*
    809 //    is returned.
    810 //
    811 //  - If the request is found to be ambiguous, the string `"ambiguous"` is
    812 //    returned.
    813 //
    814 static bool ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module,
    815                                Handle<JSAtom*> exportName,
    816                                MutableHandle<Value> result,
    817                                ModuleErrorInfo* errorInfoOut = nullptr) {
    818  // Step 1. Assert: module.[[Status]] is not new.
    819  MOZ_ASSERT(module->status() != ModuleStatus::New);
    820 
    821  // Step 2. If resolveSet is not present, set resolveSet to a new empty List.
    822  Rooted<ResolveSet> resolveSet(cx);
    823  return ModuleResolveExportImpl(cx, module, exportName, &resolveSet, result,
    824                                 errorInfoOut);
    825 }
    826 
    827 static bool CreateResolvedBindingObject(JSContext* cx,
    828                                        Handle<ModuleObject*> module,
    829                                        Handle<JSAtom*> bindingName,
    830                                        MutableHandle<Value> result) {
    831  ResolvedBindingObject* obj =
    832      ResolvedBindingObject::create(cx, module, bindingName);
    833  if (!obj) {
    834    return false;
    835  }
    836 
    837  result.setObject(*obj);
    838  return true;
    839 }
    840 
    841 static bool CyclicModuleResolveExport(JSContext* cx,
    842                                      Handle<ModuleObject*> module,
    843                                      Handle<JSAtom*> exportName,
    844                                      MutableHandle<ResolveSet> resolveSet,
    845                                      MutableHandle<Value> result,
    846                                      ModuleErrorInfo* errorInfoOut) {
    847  // Step 2. For each Record { [[Module]], [[ExportName]] } r of resolveSet, do:
    848  for (const auto& entry : resolveSet) {
    849    // Step 3.a. If module and r.[[Module]] are the same Module Record and
    850    //           exportName is r.[[ExportName]], then:
    851    if (entry.module() == module && entry.exportName() == exportName) {
    852      // Step 3.a.i. Assert: This is a circular import request.
    853      // Step 3.a.ii. Return null.
    854      result.setNull();
    855      if (errorInfoOut) {
    856        errorInfoOut->setCircularImport(cx, module);
    857      }
    858      return true;
    859    }
    860  }
    861 
    862  // Step 4. Append the Record { [[Module]]: module, [[ExportName]]: exportName
    863  //         } to resolveSet.
    864  if (!resolveSet.emplaceBack(module, exportName)) {
    865    ReportOutOfMemory(cx);
    866    return false;
    867  }
    868 
    869  // Step 5. For each ExportEntry Record e of module.[[LocalExportEntries]], do:
    870  for (const ExportEntry& e : module->localExportEntries()) {
    871    // Step 5.a. If e.[[ExportName]] is exportName, then:
    872    if (exportName == e.exportName()) {
    873      // Step 5.a.i. Assert: module provides the direct binding for this export.
    874      // Step 5.a.ii. Return ResolvedBinding Record { [[Module]]: module,
    875      //              [[BindingName]]: e.[[LocalName]] }.
    876      Rooted<JSAtom*> localName(cx, e.localName());
    877      return CreateResolvedBindingObject(cx, module, localName, result);
    878    }
    879  }
    880 
    881  // Step 6. For each ExportEntry Record e of module.[[IndirectExportEntries]],
    882  //         do:
    883  Rooted<ModuleRequestObject*> moduleRequest(cx);
    884  Rooted<ModuleObject*> importedModule(cx);
    885  Rooted<JSAtom*> name(cx);
    886  for (const ExportEntry& e : module->indirectExportEntries()) {
    887    // Step 6.a. If e.[[ExportName]] is exportName, then:
    888    if (exportName == e.exportName()) {
    889      // Step 6.a.i. Assert: e.[[ModuleRequest]] is not null.
    890      MOZ_ASSERT(e.moduleRequest());
    891 
    892      // Step 6.a.ii. Let importedModule be ? GetImportedModule(module,
    893      //              e.[[ModuleRequest]]).
    894      moduleRequest = e.moduleRequest();
    895      importedModule = GetImportedModule(cx, module, moduleRequest);
    896      if (!importedModule) {
    897        return false;
    898      }
    899      MOZ_ASSERT(importedModule->status() >= ModuleStatus::Unlinked);
    900 
    901      // Step 6.a.iii. If e.[[ImportName]] is ALL, then:
    902      if (!e.importName()) {
    903        // Step 6.a.iii.1. Assert: module does not provide the direct binding
    904        //                 for this export.
    905        // Step 6.a.iii.2. Return ResolvedBinding Record { [[Module]]:
    906        //                 importedModule, [[BindingName]]: NAMESPACE }.
    907        name = cx->names().star_namespace_star_;
    908        return CreateResolvedBindingObject(cx, importedModule, name, result);
    909      } else {
    910        // Step 6.a.iv.1. Assert: module imports a specific binding for this
    911        //                export.
    912        // Step 6.a.iv.2. Return ? importedModule.ResolveExport(e.[[ImportName]]
    913        //                , resolveSet).
    914        name = e.importName();
    915 
    916        return ModuleResolveExportImpl(cx, importedModule, name, resolveSet,
    917                                       result, errorInfoOut);
    918      }
    919    }
    920  }
    921 
    922  // Step 7. If exportName is "default"), then:
    923  if (exportName == cx->names().default_) {
    924    // Step 7.a. Assert: A default export was not explicitly defined by this
    925    //           module.
    926    // Step 7.b. Return null.
    927    // Step 7.c. NOTE: A default export cannot be provided by an export * from
    928    //           "mod" declaration.
    929    result.setNull();
    930    if (errorInfoOut) {
    931      errorInfoOut->setImportedModule(cx, module);
    932    }
    933    return true;
    934  }
    935 
    936  // Step 8. Let starResolution be null.
    937  Rooted<ResolvedBindingObject*> starResolution(cx);
    938 
    939  // Step 9. For each ExportEntry Record e of module.[[StarExportEntries]], do:
    940  Rooted<Value> resolution(cx);
    941  Rooted<ResolvedBindingObject*> binding(cx);
    942  for (const ExportEntry& e : module->starExportEntries()) {
    943    // Step 9.a. Assert: e.[[ModuleRequest]] is not null.
    944    MOZ_ASSERT(e.moduleRequest());
    945 
    946    // Step 9.b. Let importedModule be ? GetImportedModule(module,
    947    //           e.[[ModuleRequest]]).
    948    moduleRequest = e.moduleRequest();
    949    importedModule = GetImportedModule(cx, module, moduleRequest);
    950    if (!importedModule) {
    951      return false;
    952    }
    953    MOZ_ASSERT(importedModule->status() >= ModuleStatus::Unlinked);
    954 
    955    // Step 9.c. Let resolution be ? importedModule.ResolveExport(exportName,
    956    //           resolveSet).
    957    if (!CyclicModuleResolveExport(cx, importedModule, exportName, resolveSet,
    958                                   &resolution, errorInfoOut)) {
    959      return false;
    960    }
    961 
    962    // Step 9.d. If resolution is AMBIGUOUS, return AMBIGUOUS.
    963    if (resolution == StringValue(cx->names().ambiguous)) {
    964      result.set(resolution);
    965      return true;
    966    }
    967 
    968    // Step 9.e. If resolution is not null, then:
    969    if (!resolution.isNull()) {
    970      // Step 9.e.i. Assert: resolution is a ResolvedBinding Record.
    971      binding = &resolution.toObject().as<ResolvedBindingObject>();
    972 
    973      // Step 9.e.ii. If starResolution is null, set starResolution to
    974      // resolution.
    975      if (!starResolution) {
    976        starResolution = binding;
    977      } else {
    978        // Step 9.e.iii. Else:
    979        // Step 9.e.iii.1. Assert: There is more than one * import that includes
    980        //                 the requested name.
    981        // Step 9.e.iii.2. If resolution.[[Module]] and
    982        //                 starResolution.[[Module]] are not the same Module
    983        //                 Record, return AMBIGUOUS.
    984        // Step 9.e.iii.3. If resolution.[[BindingName]] is not
    985        //                 starResolution.[[BindingName]] and either
    986        //                 resolution.[[BindingName]] or
    987        //                 starResolution.[[BindingName]] is namespace, return
    988        //                 AMBIGUOUS.
    989        // Step 9.e.iii.4. If resolution.[[BindingName]] is a String,
    990        //                 starResolution.[[BindingName]] is a String, and
    991        //                 resolution.[[BindingName]] is not
    992        //                 starResolution.[[BindingName]]), return AMBIGUOUS.
    993        if (binding->module() != starResolution->module() ||
    994            binding->bindingName() != starResolution->bindingName()) {
    995          result.set(StringValue(cx->names().ambiguous));
    996 
    997          if (errorInfoOut) {
    998            ModuleObject* module1 = starResolution->module();
    999            ModuleObject* module2 = binding->module();
   1000            errorInfoOut->setForAmbiguousImport(cx, module, module1, module2);
   1001          }
   1002          return true;
   1003        }
   1004      }
   1005    }
   1006  }
   1007 
   1008  // Step 10. Return starResolution.
   1009  result.setObjectOrNull(starResolution);
   1010  if (!starResolution && errorInfoOut) {
   1011    errorInfoOut->setImportedModule(cx, module);
   1012  }
   1013  return true;
   1014 }
   1015 
   1016 // https://tc39.es/proposal-json-modules/#sec-smr-resolveexport
   1017 static bool SyntheticModuleResolveExport(JSContext* cx,
   1018                                         Handle<ModuleObject*> module,
   1019                                         Handle<JSAtom*> exportName,
   1020                                         MutableHandle<Value> result,
   1021                                         ModuleErrorInfo* errorInfoOut) {
   1022  // Step 2. If module.[[ExportNames]] does not contain exportName, return null.
   1023  if (!ContainsElement(module->syntheticExportNames(), exportName)) {
   1024    result.setNull();
   1025    if (errorInfoOut) {
   1026      errorInfoOut->setImportedModule(cx, module);
   1027    }
   1028    return true;
   1029  }
   1030 
   1031  // Step 3. Return ResolvedBinding Record { [[Module]]: module,
   1032  // [[BindingName]]: exportName }.
   1033  return CreateResolvedBindingObject(cx, module, exportName, result);
   1034 }
   1035 
   1036 // https://tc39.es/ecma262/#sec-getmodulenamespace
   1037 // ES2023 16.2.1.10 GetModuleNamespace
   1038 ModuleNamespaceObject* js::GetOrCreateModuleNamespace(
   1039    JSContext* cx, Handle<ModuleObject*> module) {
   1040  // Step 1. Assert: If module is a Cyclic Module Record, then module.[[Status]]
   1041  //         is not new or unlinked.
   1042  MOZ_ASSERT(module->status() != ModuleStatus::New ||
   1043             module->status() != ModuleStatus::Unlinked);
   1044 
   1045  // Step 2. Let namespace be module.[[Namespace]].
   1046  Rooted<ModuleNamespaceObject*> ns(cx, module->namespace_());
   1047 
   1048  // Step 3. If namespace is empty, then:
   1049  if (!ns) {
   1050    // Step 3.a. Let exportedNames be ? module.GetExportedNames().
   1051    Rooted<ModuleSet> exportStarSet(cx);
   1052    Rooted<ExportNameVector> exportedNames(cx);
   1053    if (!ModuleGetExportedNames(cx, module, &exportStarSet, &exportedNames)) {
   1054      return nullptr;
   1055    }
   1056 
   1057    // Step 3.b. Let unambiguousNames be a new empty List.
   1058    Rooted<UniquePtr<ExportNameVector>> unambiguousNames(
   1059        cx, cx->make_unique<ExportNameVector>());
   1060    if (!unambiguousNames) {
   1061      return nullptr;
   1062    }
   1063 
   1064    // Step 3.c. For each element name of exportedNames, do:
   1065    Rooted<JSAtom*> name(cx);
   1066    Rooted<Value> resolution(cx);
   1067    for (JSAtom* atom : exportedNames) {
   1068      name = atom;
   1069 
   1070      // Step 3.c.i. Let resolution be ? module.ResolveExport(name).
   1071      if (!ModuleResolveExport(cx, module, name, &resolution)) {
   1072        return nullptr;
   1073      }
   1074 
   1075      // Step 3.c.ii. If resolution is a ResolvedBinding Record, append name to
   1076      //              unambiguousNames.
   1077      if (resolution.isObject() && !unambiguousNames->append(name)) {
   1078        ReportOutOfMemory(cx);
   1079        return nullptr;
   1080      }
   1081    }
   1082 
   1083    // Step 3.d. Set namespace to ModuleNamespaceCreate(module,
   1084    //           unambiguousNames).
   1085    ns = ModuleNamespaceCreate(cx, module, &unambiguousNames);
   1086  }
   1087 
   1088  // Step 4. Return namespace.
   1089  return ns;
   1090 }
   1091 
   1092 static bool IsResolvedBinding(JSContext* cx, Handle<Value> resolution) {
   1093  MOZ_ASSERT(resolution.isObjectOrNull() ||
   1094             resolution.toString() == cx->names().ambiguous);
   1095  return resolution.isObject();
   1096 }
   1097 
   1098 static void InitNamespaceBinding(JSContext* cx,
   1099                                 Handle<ModuleEnvironmentObject*> env,
   1100                                 Handle<JSAtom*> name,
   1101                                 Handle<ModuleNamespaceObject*> ns) {
   1102  // The property already exists in the evironment but is not writable, so set
   1103  // the slot directly.
   1104  RootedId id(cx, AtomToId(name));
   1105  mozilla::Maybe<PropertyInfo> prop = env->lookup(cx, id);
   1106  MOZ_ASSERT(prop.isSome());
   1107  env->setSlot(prop->slot(), ObjectValue(*ns));
   1108 }
   1109 
   1110 struct AtomComparator {
   1111  bool operator()(JSAtom* a, JSAtom* b, bool* lessOrEqualp) {
   1112    int32_t result = CompareStrings(a, b);
   1113    *lessOrEqualp = (result <= 0);
   1114    return true;
   1115  }
   1116 };
   1117 
   1118 // https://tc39.es/ecma262/#sec-modulenamespacecreate
   1119 // ES2023 10.4.6.12 ModuleNamespaceCreate
   1120 static ModuleNamespaceObject* ModuleNamespaceCreate(
   1121    JSContext* cx, Handle<ModuleObject*> module,
   1122    MutableHandle<UniquePtr<ExportNameVector>> exports) {
   1123  // Step 1. Assert: module.[[Namespace]] is empty.
   1124  MOZ_ASSERT(!module->namespace_());
   1125 
   1126  // Step 6. Let sortedExports be a List whose elements are the elements of
   1127  //         exports ordered as if an Array of the same values had been sorted
   1128  //         using %Array.prototype.sort% using undefined as comparefn.
   1129  ExportNameVector scratch;
   1130  if (!scratch.resize(exports->length())) {
   1131    ReportOutOfMemory(cx);
   1132    return nullptr;
   1133  }
   1134  MOZ_ALWAYS_TRUE(MergeSort(exports->begin(), exports->length(),
   1135                            scratch.begin(), AtomComparator()));
   1136 
   1137  // Steps 2 - 5.
   1138  Rooted<ModuleNamespaceObject*> ns(
   1139      cx, ModuleObject::createNamespace(cx, module, exports));
   1140  if (!ns) {
   1141    return nullptr;
   1142  }
   1143 
   1144  // Pre-compute all binding mappings now instead of on each access.
   1145  // See:
   1146  // https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-get-p-receiver
   1147  // ES2023 10.4.6.8 Module Namespace Exotic Object [[Get]]
   1148  Rooted<JSAtom*> name(cx);
   1149  Rooted<Value> resolution(cx);
   1150  Rooted<ResolvedBindingObject*> binding(cx);
   1151  Rooted<ModuleObject*> importedModule(cx);
   1152  Rooted<ModuleNamespaceObject*> importedNamespace(cx);
   1153  Rooted<JSAtom*> bindingName(cx);
   1154  for (JSAtom* atom : ns->exports()) {
   1155    name = atom;
   1156 
   1157    if (!ModuleResolveExport(cx, module, name, &resolution)) {
   1158      return nullptr;
   1159    }
   1160 
   1161    MOZ_ASSERT(IsResolvedBinding(cx, resolution));
   1162    binding = &resolution.toObject().as<ResolvedBindingObject>();
   1163    importedModule = binding->module();
   1164    bindingName = binding->bindingName();
   1165 
   1166    if (bindingName == cx->names().star_namespace_star_) {
   1167      importedNamespace = GetOrCreateModuleNamespace(cx, importedModule);
   1168      if (!importedNamespace) {
   1169        return nullptr;
   1170      }
   1171 
   1172      // The spec uses an immutable binding here but we have already generated
   1173      // bytecode for an indirect binding. Instead, use an indirect binding to
   1174      // "*namespace*" slot of the target environment.
   1175      Rooted<ModuleEnvironmentObject*> env(
   1176          cx, &importedModule->initialEnvironment());
   1177      InitNamespaceBinding(cx, env, bindingName, importedNamespace);
   1178    }
   1179 
   1180    if (!ns->addBinding(cx, name, importedModule, bindingName)) {
   1181      return nullptr;
   1182    }
   1183  }
   1184 
   1185  // Step 10. Return M.
   1186  return ns;
   1187 }
   1188 
   1189 void ModuleErrorInfo::setImportedModule(JSContext* cx,
   1190                                        ModuleObject* importedModule) {
   1191  imported = importedModule->filename();
   1192 }
   1193 
   1194 void ModuleErrorInfo::setCircularImport(JSContext* cx,
   1195                                        ModuleObject* importedModule) {
   1196  setImportedModule(cx, importedModule);
   1197  isCircular = true;
   1198 }
   1199 
   1200 void ModuleErrorInfo::setForAmbiguousImport(JSContext* cx,
   1201                                            ModuleObject* importedModule,
   1202                                            ModuleObject* module1,
   1203                                            ModuleObject* module2) {
   1204  setImportedModule(cx, importedModule);
   1205  entry1 = module1->filename();
   1206  entry2 = module2->filename();
   1207 }
   1208 
   1209 static void CreateErrorNumberMessageUTF8(JSContext* cx, unsigned errorNumber,
   1210                                         JSErrorReport* reportOut, ...) {
   1211  va_list ap;
   1212  va_start(ap, reportOut);
   1213  AutoReportFrontendContext fc(cx);
   1214  if (!ExpandErrorArgumentsVA(&fc, GetErrorMessage, nullptr, errorNumber,
   1215                              ArgumentsAreUTF8, reportOut, ap)) {
   1216    ReportOutOfMemory(cx);
   1217    return;
   1218  }
   1219 
   1220  va_end(ap);
   1221 }
   1222 
   1223 static void ThrowResolutionError(JSContext* cx, Handle<ModuleObject*> module,
   1224                                 Handle<Value> resolution, Handle<JSAtom*> name,
   1225                                 ModuleErrorInfo* errorInfo) {
   1226  MOZ_ASSERT(errorInfo);
   1227  auto chars = StringToNewUTF8CharsZ(cx, *name);
   1228  if (!chars) {
   1229    ReportOutOfMemory(cx);
   1230    return;
   1231  }
   1232 
   1233  bool isAmbiguous = resolution == StringValue(cx->names().ambiguous);
   1234 
   1235  unsigned errorNumber;
   1236  if (errorInfo->isCircular) {
   1237    errorNumber = JSMSG_MODULE_CIRCULAR_IMPORT;
   1238  } else if (isAmbiguous) {
   1239    errorNumber = JSMSG_MODULE_AMBIGUOUS;
   1240  } else {
   1241    errorNumber = JSMSG_MODULE_NO_EXPORT;
   1242  }
   1243 
   1244  JSErrorReport report;
   1245  report.isWarning_ = false;
   1246  report.errorNumber = errorNumber;
   1247 
   1248  if (errorNumber == JSMSG_MODULE_AMBIGUOUS) {
   1249    CreateErrorNumberMessageUTF8(cx, errorNumber, &report, errorInfo->imported,
   1250                                 chars.get(), errorInfo->entry1,
   1251                                 errorInfo->entry2);
   1252  } else {
   1253    CreateErrorNumberMessageUTF8(cx, errorNumber, &report, errorInfo->imported,
   1254                                 chars.get());
   1255  }
   1256 
   1257  Rooted<JSString*> message(cx, report.newMessageString(cx));
   1258  if (!message) {
   1259    ReportOutOfMemory(cx);
   1260    return;
   1261  }
   1262 
   1263  const char* file = module->filename();
   1264  RootedString filename(
   1265      cx, JS_NewStringCopyUTF8Z(cx, JS::ConstUTF8CharsZ(file, strlen(file))));
   1266  if (!filename) {
   1267    ReportOutOfMemory(cx);
   1268    return;
   1269  }
   1270 
   1271  RootedValue error(cx);
   1272  if (!JS::CreateError(cx, JSEXN_SYNTAXERR, nullptr, filename,
   1273                       errorInfo->lineNumber, errorInfo->columnNumber, nullptr,
   1274                       message, JS::NothingHandleValue, &error)) {
   1275    ReportOutOfMemory(cx);
   1276    return;
   1277  }
   1278 
   1279  cx->setPendingException(error, nullptr);
   1280 }
   1281 
   1282 // https://tc39.es/ecma262/#sec-source-text-module-record-initialize-environment
   1283 // ES2023 16.2.1.6.4 InitializeEnvironment
   1284 static bool ModuleInitializeEnvironment(JSContext* cx,
   1285                                        Handle<ModuleObject*> module) {
   1286  MOZ_ASSERT(module->status() == ModuleStatus::Linking);
   1287 
   1288  // Step 1. For each ExportEntry Record e of module.[[IndirectExportEntries]],
   1289  //         do:
   1290  Rooted<JSAtom*> exportName(cx);
   1291  Rooted<Value> resolution(cx);
   1292  for (const ExportEntry& e : module->indirectExportEntries()) {
   1293    // Step 1.a. Assert: e.[[ExportName]] is not null.
   1294    MOZ_ASSERT(e.exportName());
   1295 
   1296    // Step 1.b. Let resolution be ? module.ResolveExport(e.[[ExportName]]).
   1297    exportName = e.exportName();
   1298    ModuleErrorInfo errorInfo{e.lineNumber(), e.columnNumber()};
   1299    if (!ModuleResolveExport(cx, module, exportName, &resolution, &errorInfo)) {
   1300      return false;
   1301    }
   1302 
   1303    // Step 1.c. If resolution is either null or AMBIGUOUS, throw a SyntaxError
   1304    //           exception.
   1305    if (!IsResolvedBinding(cx, resolution)) {
   1306      ThrowResolutionError(cx, module, resolution, exportName, &errorInfo);
   1307      return false;
   1308    }
   1309  }
   1310 
   1311  // Step 5. Let env be NewModuleEnvironment(realm.[[GlobalEnv]]).
   1312  // Step 6. Set module.[[Environment]] to env.
   1313  // Note that we have already created the environment by this point.
   1314  Rooted<ModuleEnvironmentObject*> env(cx, &module->initialEnvironment());
   1315 
   1316  // Step 7. For each ImportEntry Record in of module.[[ImportEntries]], do:
   1317  Rooted<ModuleRequestObject*> moduleRequest(cx);
   1318  Rooted<ModuleObject*> importedModule(cx);
   1319  Rooted<JSAtom*> importName(cx);
   1320  Rooted<JSAtom*> localName(cx);
   1321  Rooted<ModuleObject*> sourceModule(cx);
   1322  Rooted<JSAtom*> bindingName(cx);
   1323  for (const ImportEntry& in : module->importEntries()) {
   1324    // Step 7.a. Let importedModule be ! GetImportedModule(module,
   1325    //           in.[[ModuleRequest]]).
   1326    moduleRequest = in.moduleRequest();
   1327    importedModule = GetImportedModule(cx, module, moduleRequest);
   1328    if (!importedModule) {
   1329      return false;
   1330    }
   1331    MOZ_ASSERT(importedModule->status() >= ModuleStatus::Linking);
   1332 
   1333    localName = in.localName();
   1334    importName = in.importName();
   1335 
   1336    // Step 7.c. If in.[[ImportName]] is namespace-object, then:
   1337    if (!importName) {
   1338      // Step 7.c.i. Let namespace be ? GetModuleNamespace(importedModule).
   1339      Rooted<ModuleNamespaceObject*> ns(
   1340          cx, GetOrCreateModuleNamespace(cx, importedModule));
   1341      if (!ns) {
   1342        return false;
   1343      }
   1344 
   1345      // Step 7.c.ii. Perform ! env.CreateImmutableBinding(in.[[LocalName]],
   1346      // true). This happens when the environment is created.
   1347 
   1348      // Step 7.c.iii. Perform ! env.InitializeBinding(in.[[LocalName]],
   1349      // namespace).
   1350      InitNamespaceBinding(cx, env, localName, ns);
   1351    } else {
   1352      // Step 7.d. Else:
   1353      // Step 7.d.i. Let resolution be ?
   1354      // importedModule.ResolveExport(in.[[ImportName]]).
   1355      ModuleErrorInfo errorInfo{in.lineNumber(), in.columnNumber()};
   1356      if (!ModuleResolveExport(cx, importedModule, importName, &resolution,
   1357                               &errorInfo)) {
   1358        return false;
   1359      }
   1360 
   1361      // Step 7.d.ii. If resolution is null or ambiguous, throw a SyntaxError
   1362      //              exception.
   1363      if (!IsResolvedBinding(cx, resolution)) {
   1364        ThrowResolutionError(cx, module, resolution, importName, &errorInfo);
   1365        return false;
   1366      }
   1367 
   1368      auto* binding = &resolution.toObject().as<ResolvedBindingObject>();
   1369      sourceModule = binding->module();
   1370      bindingName = binding->bindingName();
   1371 
   1372      // Step 7.d.iii. If resolution.[[BindingName]] is namespace, then:
   1373      if (bindingName == cx->names().star_namespace_star_) {
   1374        // Step 7.d.iii.1. Let namespace be ?
   1375        //                 GetModuleNamespace(resolution.[[Module]]).
   1376        Rooted<ModuleNamespaceObject*> ns(
   1377            cx, GetOrCreateModuleNamespace(cx, sourceModule));
   1378        if (!ns) {
   1379          return false;
   1380        }
   1381 
   1382        // Step 7.d.iii.2. Perform !
   1383        //                 env.CreateImmutableBinding(in.[[LocalName]], true).
   1384        // Step 7.d.iii.3. Perform ! env.InitializeBinding(in.[[LocalName]],
   1385        //                 namespace).
   1386        //
   1387        // This should be InitNamespaceBinding, but we have already generated
   1388        // bytecode assuming an indirect binding. Instead, ensure a special
   1389        // "*namespace*"" binding exists on the target module's environment. We
   1390        // then generate an indirect binding to this synthetic binding.
   1391        Rooted<ModuleEnvironmentObject*> sourceEnv(
   1392            cx, &sourceModule->initialEnvironment());
   1393        InitNamespaceBinding(cx, sourceEnv, bindingName, ns);
   1394        if (!env->createImportBinding(cx, localName, sourceModule,
   1395                                      bindingName)) {
   1396          return false;
   1397        }
   1398      } else {
   1399        // Step 7.d.iv. Else:
   1400        // Step 7.d.iv.1. 1. Perform env.CreateImportBinding(in.[[LocalName]],
   1401        //                   resolution.[[Module]], resolution.[[BindingName]]).
   1402        if (!env->createImportBinding(cx, localName, sourceModule,
   1403                                      bindingName)) {
   1404          return false;
   1405        }
   1406      }
   1407    }
   1408  }
   1409 
   1410  // Steps 8-26.
   1411  //
   1412  // Some of these do not need to happen for practical purposes. For steps
   1413  // 21-23, the bindings that can be handled in a similar way to regulars
   1414  // scripts are done separately. Function Declarations are special due to
   1415  // hoisting and are handled within this function. See ModuleScope and
   1416  // ModuleEnvironmentObject for further details.
   1417 
   1418  // Step 24. For each element d of lexDeclarations, do:
   1419  // Step 24.a. For each element dn of the BoundNames of d, do:
   1420  // Step 24.a.iii. If d is a FunctionDeclaration, a GeneratorDeclaration, an
   1421  //                AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration,
   1422  //                then:
   1423  // Step 24.a.iii.1 Let fo be InstantiateFunctionObject of d with arguments env
   1424  //                 and privateEnv.
   1425  // Step 24.a.iii.2. Perform ! env.InitializeBinding(dn, fo).
   1426  return ModuleObject::instantiateFunctionDeclarations(cx, module);
   1427 }
   1428 
   1429 static bool FailWithUnsupportedAttributeException(
   1430    JSContext* cx, Handle<GraphLoadingStateRecordObject*> state,
   1431    Handle<ModuleRequestObject*> moduleRequest) {
   1432  UniqueChars printableKey = AtomToPrintableString(
   1433      cx, moduleRequest->getFirstUnsupportedAttributeKey());
   1434  JS_ReportErrorNumberASCII(
   1435      cx, GetErrorMessage, nullptr,
   1436      JSMSG_IMPORT_ATTRIBUTES_STATIC_IMPORT_UNSUPPORTED_ATTRIBUTE,
   1437      printableKey ? printableKey.get() : "");
   1438 
   1439  JS::ExceptionStack exnStack(cx);
   1440  if (!JS::StealPendingExceptionStack(cx, &exnStack)) {
   1441    return false;
   1442  }
   1443 
   1444  return ContinueModuleLoading(cx, state, nullptr, ImportPhase::Evaluation,
   1445                               exnStack.exception());
   1446 }
   1447 
   1448 // https://tc39.es/ecma262/#sec-InnerModuleLoading
   1449 // InnerModuleLoading ( state, module )
   1450 static bool InnerModuleLoading(JSContext* cx,
   1451                               Handle<GraphLoadingStateRecordObject*> state,
   1452                               Handle<ModuleObject*> module) {
   1453  MOZ_ASSERT(state);
   1454  MOZ_ASSERT(module);
   1455 
   1456  // Step 1. Assert: state.[[IsLoading]] is true.
   1457  MOZ_ASSERT(state->isLoading());
   1458 
   1459  // Step 2. If module is a Cyclic Module Record, module.[[Status]] is new, and
   1460  // state.[[Visited]] does not contain module, then
   1461  if (module->hasCyclicModuleFields() &&
   1462      module->status() == ModuleStatus::New && !state->visited().has(module)) {
   1463    // Step 2.a. Append module to state.[[Visited]].
   1464    if (!state->visited().putNew(module)) {
   1465      ReportOutOfMemory(cx);
   1466      return false;
   1467    }
   1468 
   1469    // Step 2.b. Let requestedModulesCount be the number of elements in
   1470    //           module.[[RequestedModules]].
   1471    size_t requestedModulesCount = module->requestedModules().Length();
   1472 
   1473    // Step 2.c. Set state.[[PendingModulesCount]] to
   1474    //           state.[[PendingModulesCount]] + requestedModulesCount.
   1475    uint32_t count = state->pendingModulesCount() + requestedModulesCount;
   1476    state->setPendingModulesCount(count);
   1477 
   1478    // Step 2.d. For each String required of module.[[RequestedModules]], do
   1479    Rooted<ModuleRequestObject*> moduleRequest(cx);
   1480    Rooted<ModuleObject*> recordModule(cx);
   1481    Rooted<JSAtom*> invalidKey(cx);
   1482    for (const RequestedModule& request : module->requestedModules()) {
   1483      moduleRequest = request.moduleRequest();
   1484 
   1485      // https://tc39.es/proposal-import-attributes/#sec-InnerModuleLoading
   1486      if (moduleRequest->hasFirstUnsupportedAttributeKey()) {
   1487        if (!FailWithUnsupportedAttributeException(cx, state, moduleRequest)) {
   1488          return false;
   1489        }
   1490      } else if (auto record = module->loadedModules().lookup(moduleRequest)) {
   1491        // Step 2.d.i. If module.[[LoadedModules]] contains a Record whose
   1492        //             [[Specifier]] is required, then
   1493        // Step 2.d.i.1. Let record be that Record.
   1494        // Step 2.d.i.2. Perform InnerModuleLoading(state, record.[[Module]]).
   1495        recordModule = record->value();
   1496        if (!InnerModuleLoading(cx, state, recordModule)) {
   1497          return false;
   1498        }
   1499      } else {
   1500        // Step 2.d.ii. Else,
   1501        // Step 2.d.ii.1. Perform HostLoadImportedModule(module, required,
   1502        //                state.[[HostDefined]], state).
   1503        Rooted<JSScript*> referrer(cx, module->script());
   1504        Rooted<Value> hostDefined(cx, state->hostDefined());
   1505        Rooted<Value> payload(cx, ObjectValue(*state));
   1506        if (!HostLoadImportedModule(cx, referrer, moduleRequest, hostDefined,
   1507                                    payload, request.lineNumber(),
   1508                                    request.columnNumber())) {
   1509          return false;
   1510        }
   1511      }
   1512 
   1513      // Step 2.d.iii. If state.[[IsLoading]] is false, return unused.
   1514      if (!state->isLoading()) {
   1515        return true;
   1516      }
   1517    }
   1518  }
   1519 
   1520  // Step 3. Assert: state.[[PendingModulesCount]] ≥ 1.
   1521  MOZ_ASSERT(state->pendingModulesCount() >= 1);
   1522 
   1523  // Step 4. Set state.[[PendingModulesCount]] to
   1524  //         state.[[PendingModulesCount]] - 1.
   1525  uint32_t count = state->pendingModulesCount() - 1;
   1526  state->setPendingModulesCount(count);
   1527 
   1528  // Step 5. If state.[[PendingModulesCount]] = 0, then
   1529  if (state->pendingModulesCount() == 0) {
   1530    // Step 5.a. Set state.[[IsLoading]] to false.
   1531    state->setIsLoading(false);
   1532 
   1533    // Step 5.b. For each Cyclic Module Record loaded of state.[[Visited]], do
   1534    for (auto iter = state->visited().iter(); !iter.done(); iter.next()) {
   1535      auto& loaded = iter.get();
   1536      // Step 5.b.i. If loaded.[[Status]] is new, set loaded.[[Status]] to
   1537      // unlinked.
   1538      if (loaded->status() == ModuleStatus::New) {
   1539        loaded->setStatus(ModuleStatus::Unlinked);
   1540      }
   1541    }
   1542 
   1543    // Step 5.c. Perform ! Call(state.[[PromiseCapability]].[[Resolve]],
   1544    //                          undefined, « undefined »).
   1545    RootedValue hostDefined(cx, state->hostDefined());
   1546    if (!state->resolved(cx, hostDefined)) {
   1547      return false;
   1548    }
   1549  }
   1550 
   1551  // Step 6. Return unused.
   1552  return true;
   1553 }
   1554 
   1555 // https://tc39.es/ecma262/#sec-ContinueModuleLoading
   1556 // ContinueModuleLoading ( state, moduleCompletion )
   1557 static bool ContinueModuleLoading(JSContext* cx,
   1558                                  Handle<GraphLoadingStateRecordObject*> state,
   1559                                  Handle<ModuleObject*> moduleCompletion,
   1560                                  ImportPhase phase, Handle<Value> error) {
   1561  MOZ_ASSERT_IF(moduleCompletion, error.isUndefined());
   1562  MOZ_ASSERT(phase < ImportPhase::Limit);
   1563 
   1564  // Step 1. If state.[[IsLoading]] is false, return unused.
   1565  if (!state->isLoading()) {
   1566    return true;
   1567  }
   1568 
   1569  // Step 2. If moduleCompletion is a normal completion, then
   1570  if (moduleCompletion) {
   1571    // TODO: Bug 1943933: Implement Source Phase Imports
   1572    MOZ_ASSERT(phase == ImportPhase::Evaluation);
   1573 
   1574    // Step 2.a. Perform InnerModuleLoading(state, moduleCompletion.[[Value]]).
   1575    return InnerModuleLoading(cx, state, moduleCompletion);
   1576  }
   1577 
   1578  // Step 3. Else,
   1579  // Step 3.a. Set state.[[IsLoading]] to false.
   1580  state->setIsLoading(false);
   1581 
   1582  // Step 3.b. Perform ! Call(state.[[PromiseCapability]].[[Reject]],
   1583  // undefined, « moduleCompletion.[[Value]] »).
   1584  RootedValue hostDefined(cx, state->hostDefined());
   1585  return state->rejected(cx, hostDefined, error);
   1586 }
   1587 
   1588 // https://tc39.es/ecma262/#sec-LoadRequestedModules
   1589 bool js::LoadRequestedModules(JSContext* cx, Handle<ModuleObject*> module,
   1590                              Handle<Value> hostDefined,
   1591                              JS::LoadModuleResolvedCallback resolved,
   1592                              JS::LoadModuleRejectedCallback rejected) {
   1593  if (module->hasSyntheticModuleFields()) {
   1594    // Step 1. Return ! PromiseResolve(%Promise%, undefined).
   1595    return resolved(cx, hostDefined);
   1596  }
   1597 
   1598  // Step 1. If hostDefined is not present, let hostDefined be empty.
   1599  // Step 2. Let pc be ! NewPromiseCapability(%Promise%).
   1600  // Note: For implementation we use callbacks to notify the results.
   1601 
   1602  // Step 3. Let state be the GraphLoadingState Record { [[IsLoading]]: true,
   1603  //         [[PendingModulesCount]]: 1, [[Visited]]: « »,
   1604  //         [[PromiseCapability]]: pc, [[HostDefined]]: hostDefined }.
   1605  Rooted<GraphLoadingStateRecordObject*> state(
   1606      cx, GraphLoadingStateRecordObject::create(cx, true, 1, resolved, rejected,
   1607                                                hostDefined));
   1608  if (!state) {
   1609    ReportOutOfMemory(cx);
   1610    return false;
   1611  }
   1612 
   1613  // Step 4. Perform InnerModuleLoading(state, module).
   1614  return InnerModuleLoading(cx, state, module);
   1615 }
   1616 
   1617 bool js::LoadRequestedModules(JSContext* cx, Handle<ModuleObject*> module,
   1618                              Handle<Value> hostDefined,
   1619                              MutableHandle<JSObject*> promiseOut) {
   1620  // Step 1. If hostDefined is not present, let hostDefined be empty.
   1621  // Step 2. Let pc be ! NewPromiseCapability(%Promise%).
   1622  Rooted<PromiseObject*> pc(cx, CreatePromiseObjectForAsync(cx));
   1623  if (!pc) {
   1624    ReportOutOfMemory(cx);
   1625    return false;
   1626  }
   1627 
   1628  if (module->hasSyntheticModuleFields()) {
   1629    // Step 1. Return ! PromiseResolve(%Promise%, undefined).
   1630    promiseOut.set(pc);
   1631    return AsyncFunctionReturned(cx, pc, UndefinedHandleValue);
   1632  }
   1633 
   1634  // Step 3. Let state be the GraphLoadingState Record { [[IsLoading]]: true,
   1635  //         [[PendingModulesCount]]: 1, [[Visited]]: « »,
   1636  //         [[PromiseCapability]]: pc, [[HostDefined]]: hostDefined }.
   1637  Rooted<GraphLoadingStateRecordObject*> state(
   1638      cx, GraphLoadingStateRecordObject::create(cx, true, 1, pc, hostDefined));
   1639  if (!state) {
   1640    ReportOutOfMemory(cx);
   1641    return false;
   1642  }
   1643 
   1644  // Step 4. Perform InnerModuleLoading(state, module).
   1645  if (!InnerModuleLoading(cx, state, module)) {
   1646    return false;
   1647  }
   1648 
   1649  // Step 5. Return pc.[[Promise]].
   1650  promiseOut.set(pc);
   1651  return true;
   1652 }
   1653 
   1654 // https://tc39.es/ecma262/#sec-moduledeclarationlinking
   1655 // ES2023 16.2.1.5.1 Link
   1656 static bool ModuleLink(JSContext* cx, Handle<ModuleObject*> module) {
   1657  if (!module->hasCyclicModuleFields()) {
   1658    return true;
   1659  }
   1660 
   1661  // Step 1. Assert: module.[[Status]] is one of unlinked, linked,
   1662  //         evaluating-async, or evaluated.
   1663  ModuleStatus status = module->status();
   1664  if (status == ModuleStatus::New || status == ModuleStatus::Linking ||
   1665      status == ModuleStatus::Evaluating) {
   1666    ThrowUnexpectedModuleStatus(cx, status);
   1667    return false;
   1668  }
   1669 
   1670  // Step 2. Let stack be a new empty List.
   1671  Rooted<ModuleVector> stack(cx);
   1672 
   1673  // Step 3. Let result be Completion(InnerModuleLinking(module, stack, 0)).
   1674  size_t ignored;
   1675  bool ok = InnerModuleLinking(cx, module, &stack, 0, &ignored);
   1676 
   1677  // Step 4. If result is an abrupt completion, then:
   1678  if (!ok) {
   1679    // Step 4.a. For each Cyclic Module Record m of stack, do:
   1680    for (ModuleObject* m : stack) {
   1681      // Step 4.a.i. Assert: m.[[Status]] is linking.
   1682      MOZ_ASSERT(m->status() == ModuleStatus::Linking);
   1683      // Step 4.a.ii. Set m.[[Status]] to unlinked.
   1684      m->setStatus(ModuleStatus::Unlinked);
   1685      m->clearDfsAncestorIndex();
   1686    }
   1687 
   1688    // Step 4.b. Assert: module.[[Status]] is unlinked.
   1689    MOZ_ASSERT(module->status() == ModuleStatus::Unlinked);
   1690 
   1691    // Step 4.c.
   1692    return false;
   1693  }
   1694 
   1695  // Step 5. Assert: module.[[Status]] is linked, evaluating-async, or
   1696  //         evaluated.
   1697  MOZ_ASSERT(module->status() == ModuleStatus::Linked ||
   1698             module->status() == ModuleStatus::EvaluatingAsync ||
   1699             module->status() == ModuleStatus::Evaluated);
   1700 
   1701  // Step 6. Assert: stack is empty.
   1702  MOZ_ASSERT(stack.empty());
   1703 
   1704  // Step 7. Return unused.
   1705  return true;
   1706 }
   1707 
   1708 // https://tc39.es/ecma262/#sec-InnerModuleLinking
   1709 // ES2023 16.2.1.5.1.1 InnerModuleLinking
   1710 static bool InnerModuleLinking(JSContext* cx, Handle<ModuleObject*> module,
   1711                               MutableHandle<ModuleVector> stack, size_t index,
   1712                               size_t* indexOut) {
   1713  // Step 1. If module is not a Cyclic Module Record, then
   1714  if (!module->hasCyclicModuleFields()) {
   1715    // Step 1.a. Perform ? module.Link(). (Skipped)
   1716    // Step 2.b. Return index.
   1717    *indexOut = index;
   1718    return true;
   1719  }
   1720 
   1721  // Step 2. If module.[[Status]] is linking, linked, evaluating-async, or
   1722  //         evaluated, then:
   1723  if (module->status() == ModuleStatus::Linking ||
   1724      module->status() == ModuleStatus::Linked ||
   1725      module->status() == ModuleStatus::EvaluatingAsync ||
   1726      module->status() == ModuleStatus::Evaluated) {
   1727    // Step 2.a. Return index.
   1728    *indexOut = index;
   1729    return true;
   1730  }
   1731 
   1732  // Step 3. Assert: module.[[Status]] is unlinked.
   1733  if (module->status() != ModuleStatus::Unlinked) {
   1734    ThrowUnexpectedModuleStatus(cx, module->status());
   1735    return false;
   1736  }
   1737 
   1738  // Step 8. Append module to stack.
   1739  // Do this before changing the status so that we can recover on failure.
   1740  if (!stack.append(module)) {
   1741    ReportOutOfMemory(cx);
   1742    return false;
   1743  }
   1744 
   1745  // Step 4. Set module.[[Status]] to linking.
   1746  module->setStatus(ModuleStatus::Linking);
   1747 
   1748  // Step 5. Let moduleIndex be index.
   1749  size_t moduleIndex = index;
   1750 
   1751  // Step 6. Set module.[[DFSAncestorIndex]] to index.
   1752  module->setDfsAncestorIndex(index);
   1753 
   1754  // Step 7. Set index to index + 1.
   1755  index++;
   1756 
   1757  AutoCheckRecursionLimit recursion(cx);
   1758  if (!recursion.check(cx)) {
   1759    return false;
   1760  }
   1761 
   1762  // Step 9. For each String required that is an element of
   1763  //         module.[[RequestedModules]], do:
   1764  Rooted<ModuleRequestObject*> required(cx);
   1765  Rooted<ModuleObject*> requiredModule(cx);
   1766  for (const RequestedModule& request : module->requestedModules()) {
   1767    // Step 9.a. Let requiredModule be ? GetImportedModule(module, required).
   1768    required = request.moduleRequest();
   1769    MOZ_ASSERT(required->phase() == ImportPhase::Evaluation);
   1770    requiredModule = GetImportedModule(cx, module, required);
   1771    if (!requiredModule) {
   1772      return false;
   1773    }
   1774    MOZ_ASSERT(requiredModule->status() >= ModuleStatus::Unlinked);
   1775 
   1776    // Step 9.b. Set index to ? InnerModuleLinking(requiredModule, stack,
   1777    //           index).
   1778    if (!InnerModuleLinking(cx, requiredModule, stack, index, &index)) {
   1779      return false;
   1780    }
   1781 
   1782    // Step 9.c. If requiredModule is a Cyclic Module Record, then:
   1783    if (requiredModule->hasCyclicModuleFields()) {
   1784      // Step 9.c.i. Assert: requiredModule.[[Status]] is either linking,
   1785      // linked,
   1786      //             evaluating-async, or evaluated.
   1787      MOZ_ASSERT(requiredModule->status() == ModuleStatus::Linking ||
   1788                 requiredModule->status() == ModuleStatus::Linked ||
   1789                 requiredModule->status() == ModuleStatus::EvaluatingAsync ||
   1790                 requiredModule->status() == ModuleStatus::Evaluated);
   1791 
   1792      // Step 9.c.ii. Assert: requiredModule.[[Status]] is linking if and only
   1793      // if
   1794      //              requiredModule is in stack.
   1795      MOZ_ASSERT((requiredModule->status() == ModuleStatus::Linking) ==
   1796                 ContainsElement(stack, requiredModule));
   1797 
   1798      // Step 9.c.iii. If requiredModule.[[Status]] is linking, then:
   1799      if (requiredModule->status() == ModuleStatus::Linking) {
   1800        // Step 9.c.iii.1. Set module.[[DFSAncestorIndex]] to
   1801        //                 min(module.[[DFSAncestorIndex]],
   1802        //                 requiredModule.[[DFSAncestorIndex]]).
   1803        module->setDfsAncestorIndex(std::min(
   1804            module->dfsAncestorIndex(), requiredModule->dfsAncestorIndex()));
   1805      }
   1806    }
   1807  }
   1808 
   1809  // Step 10. Perform ? module.InitializeEnvironment().
   1810  if (!ModuleInitializeEnvironment(cx, module)) {
   1811    return false;
   1812  }
   1813 
   1814  // Step 11. Assert: module occurs exactly once in stack.
   1815  MOZ_ASSERT(CountElements(stack, module) == 1);
   1816 
   1817  // Step 12. Assert: module.[[DFSAncestorIndex]] <= moduleIndex.
   1818  MOZ_ASSERT(module->dfsAncestorIndex() <= moduleIndex);
   1819 
   1820  // Step 13. If module.[[DFSAncestorIndex]] = moduleIndex, then
   1821  if (module->dfsAncestorIndex() == moduleIndex) {
   1822    // Step 13.a.
   1823    bool done = false;
   1824 
   1825    // Step 13.b. Repeat, while done is false:
   1826    while (!done) {
   1827      // Step 13.b.i. Let requiredModule be the last element in stack.
   1828      // Step 13.b.ii. Remove the last element of stack.
   1829      requiredModule = stack.popCopy();
   1830 
   1831      // Step 13.b.iv. Set requiredModule.[[Status]] to linked.
   1832      requiredModule->setStatus(ModuleStatus::Linked);
   1833 
   1834      // Step 13.b.v. If requiredModule and module are the same Module Record,
   1835      //              set done to true.
   1836      done = requiredModule == module;
   1837    }
   1838  }
   1839 
   1840  // Step 14. Return index.
   1841  *indexOut = index;
   1842  return true;
   1843 }
   1844 
   1845 static bool SyntheticModuleEvaluate(JSContext* cx,
   1846                                    Handle<ModuleObject*> moduleArg,
   1847                                    MutableHandle<Value> rval) {
   1848  // Steps 1-12 happen elsewhere in the engine.
   1849 
   1850  // Step 13. Let pc be ! NewPromiseCapability(%Promise%).
   1851  Rooted<PromiseObject*> resultPromise(cx, CreatePromiseObjectForAsync(cx));
   1852  if (!resultPromise) {
   1853    return false;
   1854  }
   1855 
   1856  // Since the only synthetic modules we support are JSON modules, result is
   1857  // always |undefined|.
   1858 
   1859  // Step 14. IfAbruptRejectPromise(result, pc) (Skipped)
   1860 
   1861  // 15. Perform ! pc.[[Resolve]](result).
   1862  if (!AsyncFunctionReturned(cx, resultPromise, JS::UndefinedHandleValue)) {
   1863    return false;
   1864  }
   1865 
   1866  // 16. Return pc.[[Promise]].
   1867  rval.set(ObjectValue(*resultPromise));
   1868  return true;
   1869 }
   1870 
   1871 // https://tc39.es/ecma262/#sec-moduleevaluation
   1872 // ES2023 16.2.1.5.2 Evaluate
   1873 static bool ModuleEvaluate(JSContext* cx, Handle<ModuleObject*> moduleArg,
   1874                           MutableHandle<Value> result) {
   1875  Rooted<ModuleObject*> module(cx, moduleArg);
   1876 
   1877  // Step 2. Assert: module.[[Status]] is linked, evaluating-async, or
   1878  //         evaluated.
   1879  ModuleStatus status = module->status();
   1880  if (status != ModuleStatus::Linked &&
   1881      status != ModuleStatus::EvaluatingAsync &&
   1882      status != ModuleStatus::Evaluated) {
   1883    ThrowUnexpectedModuleStatus(cx, status);
   1884    return false;
   1885  }
   1886 
   1887  // Note: we return early in the error case, as the spec assumes we can get the
   1888  // cycle root of |module| which may not be available.
   1889  if (module->hadEvaluationError()) {
   1890    Rooted<PromiseObject*> capability(cx);
   1891    if (!module->hasTopLevelCapability()) {
   1892      capability = ModuleObject::createTopLevelCapability(cx, module);
   1893      if (!capability) {
   1894        return false;
   1895      }
   1896 
   1897      Rooted<Value> error(cx, module->evaluationError());
   1898      if (!ModuleObject::topLevelCapabilityReject(cx, module, error)) {
   1899        return false;
   1900      }
   1901    }
   1902 
   1903    capability = module->topLevelCapability();
   1904    MOZ_ASSERT(JS::GetPromiseState(capability) == JS::PromiseState::Rejected);
   1905    MOZ_ASSERT(JS::GetPromiseResult(capability) == module->evaluationError());
   1906    result.set(ObjectValue(*capability));
   1907    return true;
   1908  }
   1909 
   1910  // Step 3. If module.[[Status]] is evaluating-async or evaluated, set module
   1911  //         to module.[[CycleRoot]].
   1912  if (module->status() == ModuleStatus::EvaluatingAsync ||
   1913      module->status() == ModuleStatus::Evaluated) {
   1914    module = module->getCycleRoot();
   1915  }
   1916 
   1917  // Step 4. If module.[[TopLevelCapability]] is not empty, then:
   1918  if (module->hasTopLevelCapability()) {
   1919    // Step 4.a. Return module.[[TopLevelCapability]].[[Promise]].
   1920    result.set(ObjectValue(*module->topLevelCapability()));
   1921    return true;
   1922  }
   1923 
   1924  // Step 5. Let stack be a new empty List.
   1925  Rooted<ModuleVector> stack(cx);
   1926 
   1927  // Step 6. Let capability be ! NewPromiseCapability(%Promise%).
   1928  // Step 7. Set module.[[TopLevelCapability]] to capability.
   1929  Rooted<PromiseObject*> capability(
   1930      cx, ModuleObject::createTopLevelCapability(cx, module));
   1931  if (!capability) {
   1932    return false;
   1933  }
   1934 
   1935  // Step 8. Let result be Completion(InnerModuleEvaluation(module, stack, 0)).
   1936  size_t ignored;
   1937  bool ok = InnerModuleEvaluation(cx, module, &stack, 0, &ignored);
   1938 
   1939  // Step 9. f result is an abrupt completion, then:
   1940  if (!ok) {
   1941    // Attempt to take any pending exception, but make sure we still handle
   1942    // uncatchable exceptions.
   1943    Rooted<Value> error(cx);
   1944    if (cx->isExceptionPending()) {
   1945      (void)cx->getPendingException(&error);
   1946      cx->clearPendingException();
   1947    }
   1948 
   1949    // Step 9.a. For each Cyclic Module Record m of stack, do
   1950    for (ModuleObject* m : stack) {
   1951      // Step 9.a.i. Assert: m.[[Status]] is evaluating.
   1952      MOZ_ASSERT(m->status() == ModuleStatus::Evaluating);
   1953 
   1954      // Step 9.a.ii. Set m.[[Status]] to evaluated.
   1955      // Step 9.a.iii. Set m.[[EvaluationError]] to result.
   1956      m->setEvaluationError(error);
   1957    }
   1958 
   1959    // Handle OOM when appending to the stack or over-recursion errors.
   1960    if (stack.empty() && !module->hadEvaluationError()) {
   1961      module->setEvaluationError(error);
   1962    }
   1963 
   1964    // Step 9.b. Assert: module.[[Status]] is evaluated.
   1965    MOZ_ASSERT(module->status() == ModuleStatus::Evaluated);
   1966 
   1967    // Step 9.c. Assert: module.[[EvaluationError]] is result.
   1968    MOZ_ASSERT(module->evaluationError() == error);
   1969 
   1970    // Step 9.d. Perform ! Call(capability.[[Reject]], undefined,
   1971    //           result.[[Value]]).
   1972    if (!ModuleObject::topLevelCapabilityReject(cx, module, error)) {
   1973      return false;
   1974    }
   1975  } else {
   1976    // Step 10. Else:
   1977    // Step 10.a. Assert: module.[[Status]] is evaluating-async or evaluated.
   1978    MOZ_ASSERT(module->status() == ModuleStatus::EvaluatingAsync ||
   1979               module->status() == ModuleStatus::Evaluated);
   1980 
   1981    // Step 10.b. Assert: module.[[EvaluationError]] is empty.
   1982    MOZ_ASSERT(!module->hadEvaluationError());
   1983 
   1984    // Step 10.c. If module.[[Status]] is evalated, then:
   1985    if (module->status() == ModuleStatus::Evaluated) {
   1986      // Step 10.c.ii. Perform ! Call(capability.[[Resolve]], undefined,
   1987      //               undefined).
   1988      if (!ModuleObject::topLevelCapabilityResolve(cx, module)) {
   1989        return false;
   1990      }
   1991    }
   1992 
   1993    // Step 10.d. Assert: stack is empty.
   1994    MOZ_ASSERT(stack.empty());
   1995  }
   1996 
   1997  // Step 11. Return capability.[[Promise]].
   1998  result.set(ObjectValue(*capability));
   1999  return true;
   2000 }
   2001 
   2002 // https://tc39.es/ecma262/#sec-innermoduleevaluation
   2003 // 16.2.1.5.2.1 InnerModuleEvaluation
   2004 static bool InnerModuleEvaluation(JSContext* cx, Handle<ModuleObject*> module,
   2005                                  MutableHandle<ModuleVector> stack,
   2006                                  size_t index, size_t* indexOut) {
   2007  // Step 1: If module is not a Cyclic Module Record, then
   2008  if (!module->hasCyclicModuleFields()) {
   2009    // Step 1.a. Let promise be ! module.Evaluate(). (Skipped)
   2010    // Step 1.b. Assert: promise.[[PromiseState]] is not pending. (Skipped)
   2011    // Step 1.c. If promise.[[PromiseState]] is rejected, then (Skipped)
   2012    //   Step 1.c.i Return ThrowCompletion(promise.[[PromiseResult]]). (Skipped)
   2013    // Step 1.d. Return index.
   2014    *indexOut = index;
   2015    return true;
   2016  }
   2017 
   2018  // Step 2. If module.[[Status]] is evaluating-async or evaluated, then:
   2019  if (module->status() == ModuleStatus::EvaluatingAsync ||
   2020      module->status() == ModuleStatus::Evaluated) {
   2021    // Step 2.a. If module.[[EvaluationError]] is empty, return index.
   2022    if (!module->hadEvaluationError()) {
   2023      *indexOut = index;
   2024      return true;
   2025    }
   2026 
   2027    // Step 2.b. Otherwise, return ? module.[[EvaluationError]].
   2028    Rooted<Value> error(cx, module->evaluationError());
   2029    cx->setPendingException(error, ShouldCaptureStack::Maybe);
   2030    return false;
   2031  }
   2032 
   2033  // Step 3. If module.[[Status]] is evaluating, return index.
   2034  if (module->status() == ModuleStatus::Evaluating) {
   2035    *indexOut = index;
   2036    return true;
   2037  }
   2038 
   2039  // Step 4. Assert: module.[[Status]] is linked.
   2040  MOZ_ASSERT(module->status() == ModuleStatus::Linked);
   2041 
   2042  // Step 10. Append module to stack.
   2043  // Do this before changing the status so that we can recover on failure.
   2044  if (!stack.append(module)) {
   2045    ReportOutOfMemory(cx);
   2046    return false;
   2047  }
   2048 
   2049  // Step 5. Set module.[[Status]] to evaluating.
   2050  module->setStatus(ModuleStatus::Evaluating);
   2051 
   2052  // Step 6. Let moduleIndex be index.
   2053  size_t moduleIndex = index;
   2054 
   2055  // Step 7. Set module.[[DFSAncestorIndex]] to index.
   2056  module->setDfsAncestorIndex(index);
   2057 
   2058  // Step 8. Set module.[[PendingAsyncDependencies]] to 0.
   2059  module->setPendingAsyncDependencies(0);
   2060 
   2061  // Step 9. Set index to index + 1.
   2062  index++;
   2063 
   2064  // Step 11. For each String required of module.[[RequestedModules]], do:
   2065  Rooted<ModuleRequestObject*> required(cx);
   2066  Rooted<ModuleObject*> requiredModule(cx);
   2067  for (const RequestedModule& request : module->requestedModules()) {
   2068    // Step 11.a. Let requiredModule be GetImportedModule(module,
   2069    //            required).
   2070    required = request.moduleRequest();
   2071    MOZ_ASSERT(required->phase() == ImportPhase::Evaluation);
   2072    requiredModule = GetImportedModule(cx, module, required);
   2073    if (!requiredModule) {
   2074      return false;
   2075    }
   2076    MOZ_ASSERT(requiredModule->status() >= ModuleStatus::Linked);
   2077 
   2078    // Step 11.b. Set index to ? InnerModuleEvaluation(requiredModule, stack,
   2079    //            index).
   2080    if (!InnerModuleEvaluation(cx, requiredModule, stack, index, &index)) {
   2081      return false;
   2082    }
   2083 
   2084    // Step 11.c. If requiredModule is a Cyclic Module Record, then:
   2085    if (requiredModule->hasCyclicModuleFields()) {
   2086      // Step 11.c.i. Assert: requiredModule.[[Status]] is either evaluating,
   2087      //              evaluating-async, or evaluated.
   2088      MOZ_ASSERT(requiredModule->status() == ModuleStatus::Evaluating ||
   2089                 requiredModule->status() == ModuleStatus::EvaluatingAsync ||
   2090                 requiredModule->status() == ModuleStatus::Evaluated);
   2091 
   2092      // Step 11.c.ii. Assert: requiredModule.[[Status]] is evaluating if and
   2093      //               only if requiredModule is in stack.
   2094      MOZ_ASSERT((requiredModule->status() == ModuleStatus::Evaluating) ==
   2095                 ContainsElement(stack, requiredModule));
   2096 
   2097      // Step 11.c.iii. If requiredModule.[[Status]] is evaluating, then:
   2098      if (requiredModule->status() == ModuleStatus::Evaluating) {
   2099        // Step 11.c.iii.1. Set module.[[DFSAncestorIndex]] to
   2100        //                  min(module.[[DFSAncestorIndex]],
   2101        //                  requiredModule.[[DFSAncestorIndex]]).
   2102        module->setDfsAncestorIndex(std::min(
   2103            module->dfsAncestorIndex(), requiredModule->dfsAncestorIndex()));
   2104      } else {
   2105        // Step 11.c.iv. Else:
   2106        // Step 11.c.iv.1. Set requiredModule to requiredModule.[[CycleRoot]].
   2107        requiredModule = requiredModule->getCycleRoot();
   2108 
   2109        // Step 11.c.iv.2. Assert: requiredModule.[[Status]] is evaluating-async
   2110        //                 or evaluated.
   2111        MOZ_ASSERT(requiredModule->status() >= ModuleStatus::EvaluatingAsync ||
   2112                   requiredModule->status() == ModuleStatus::Evaluated);
   2113 
   2114        // Step 11.c.iv.3. If requiredModule.[[EvaluationError]] is not empty,
   2115        //                 return ? requiredModule.[[EvaluationError]].
   2116        if (requiredModule->hadEvaluationError()) {
   2117          Rooted<Value> error(cx, requiredModule->evaluationError());
   2118          cx->setPendingException(error, ShouldCaptureStack::Maybe);
   2119          return false;
   2120        }
   2121      }
   2122 
   2123      // Step 11.c.v. If requiredModule.[[AsyncEvaluationOrder]] is an integer,
   2124      // then:
   2125      if (requiredModule->asyncEvaluationOrder().isInteger()) {
   2126        // Step 11.c.v.2. Append module to
   2127        // requiredModule.[[AsyncParentModules]].
   2128        if (!ModuleObject::appendAsyncParentModule(cx, requiredModule,
   2129                                                   module)) {
   2130          return false;
   2131        }
   2132 
   2133        // Step 11.d.v.1. Set module.[[PendingAsyncDependencies]] to
   2134        //                module.[[PendingAsyncDependencies]] + 1.
   2135        module->setPendingAsyncDependencies(module->pendingAsyncDependencies() +
   2136                                            1);
   2137      }
   2138    }
   2139  }
   2140 
   2141  // Step 12. If module.[[PendingAsyncDependencies]] > 0 or module.[[HasTLA]] is
   2142  //          true, then:
   2143  if (module->pendingAsyncDependencies() > 0 || module->hasTopLevelAwait()) {
   2144    // Step 12.a. Assert: module.[[AsyncEvaluationOrder]] is unset.
   2145    MOZ_ASSERT(module->asyncEvaluationOrder().isUnset());
   2146 
   2147    // Step 12.b. Set module.[[AsyncEvaluationOrder]] to
   2148    // IncrementModuleAsyncEvaluationCount().
   2149    module->asyncEvaluationOrder().set(cx->runtime());
   2150 
   2151    // Step 12.d. If module.[[PendingAsyncDependencies]] is 0, perform
   2152    //            ExecuteAsyncModule(module).
   2153    if (module->pendingAsyncDependencies() == 0) {
   2154      if (!ExecuteAsyncModule(cx, module)) {
   2155        return false;
   2156      }
   2157    }
   2158  } else {
   2159    // Step 13. Otherwise, perform ? module.ExecuteModule().
   2160    if (!ModuleObject::execute(cx, module)) {
   2161      return false;
   2162    }
   2163  }
   2164 
   2165  // Step 14. Assert: module occurs exactly once in stack.
   2166  MOZ_ASSERT(CountElements(stack, module) == 1);
   2167 
   2168  // Step 15. Assert: module.[[DFSAncestorIndex]] <= moduleIndex.
   2169  MOZ_ASSERT(module->dfsAncestorIndex() <= moduleIndex);
   2170 
   2171  // Step 16. If module.[[DFSAncestorIndex]] = momoduleIndex, then:
   2172  if (module->dfsAncestorIndex() == moduleIndex) {
   2173    // Step 16.a. Let done be false.
   2174    bool done = false;
   2175 
   2176    // Step 16.b. Repeat, while done is false:
   2177    while (!done) {
   2178      // Step 16.b.i. Let requiredModule be the last element in stack.
   2179      // Step 16.b.ii. Remove the last element of stack.
   2180      requiredModule = stack.popCopy();
   2181 
   2182      // Step 16.b.iii. Assert: requiredModule.[[AsyncEvaluationOrder]] is
   2183      //                either an integer or unset
   2184      MOZ_ASSERT(requiredModule->asyncEvaluationOrder().isInteger() ||
   2185                 requiredModule->asyncEvaluationOrder().isUnset());
   2186 
   2187      // Step 16.b.v. If requiredModule.[[AsyncEvaluationOrder]] is unset, set
   2188      //               requiredModule.[[Status]] to evaluated.
   2189      if (requiredModule->asyncEvaluationOrder().isUnset()) {
   2190        requiredModule->setStatus(ModuleStatus::Evaluated);
   2191      } else {
   2192        // Step 16.b.vi. Otherwise, set requiredModule.[[Status]] to
   2193        //               evaluating-async.
   2194        requiredModule->setStatus(ModuleStatus::EvaluatingAsync);
   2195      }
   2196 
   2197      // Step 16.b.vii. If requiredModule and module are the same Module Record,
   2198      //                set done to true.
   2199      done = requiredModule == module;
   2200 
   2201      // Step 16.b.viii. Set requiredModule.[[CycleRoot]] to module.
   2202      requiredModule->setCycleRoot(module);
   2203    }
   2204  }
   2205 
   2206  // Step 17. Return index.
   2207  *indexOut = index;
   2208  return true;
   2209 }
   2210 
   2211 // https://tc39.es/ecma262/#sec-execute-async-module
   2212 // ES2023 16.2.1.5.2.2 ExecuteAsyncModule
   2213 static bool ExecuteAsyncModule(JSContext* cx, Handle<ModuleObject*> module) {
   2214  // Step 1. Assert: module.[[Status]] is evaluating or evaluating-async.
   2215  MOZ_ASSERT(module->status() == ModuleStatus::Evaluating ||
   2216             module->status() == ModuleStatus::EvaluatingAsync);
   2217 
   2218  // Step 2. Assert: module.[[HasTLA]] is true.
   2219  MOZ_ASSERT(module->hasTopLevelAwait());
   2220 
   2221  // Steps 3 - 8 are performed by the AsyncAwait opcode.
   2222 
   2223  // Step 9. Perform ! module.ExecuteModule(capability).
   2224  // Step 10. Return unused.
   2225  return ModuleObject::execute(cx, module);
   2226 }
   2227 
   2228 // https://tc39.es/ecma262/#sec-gather-available-ancestors
   2229 // ES2023 16.2.1.5.2.3 GatherAvailableAncestors
   2230 static bool GatherAvailableModuleAncestors(
   2231    JSContext* cx, Handle<ModuleObject*> module,
   2232    MutableHandle<ModuleVector> execList) {
   2233  MOZ_ASSERT(module->status() == ModuleStatus::EvaluatingAsync);
   2234 
   2235  // Step 1. For each Cyclic Module Record m of module.[[AsyncParentModules]],
   2236  //         do:
   2237  Rooted<ListObject*> asyncParentModules(cx, module->asyncParentModules());
   2238  Rooted<ModuleObject*> m(cx);
   2239  for (uint32_t i = 0; i != asyncParentModules->length(); i++) {
   2240    m = &asyncParentModules->getDenseElement(i).toObject().as<ModuleObject>();
   2241 
   2242    // Step 1.a. If execList does not contain m and
   2243    //           m.[[CycleRoot]].[[EvaluationError]] is empty, then:
   2244    //
   2245    // Note: we also check whether m.[[EvaluationError]] is empty since an error
   2246    // in synchronous execution can prevent the CycleRoot field from being set.
   2247    if (!m->hadEvaluationError() && !m->getCycleRoot()->hadEvaluationError() &&
   2248        !ContainsElement(execList, m)) {
   2249      // Step 1.a.i. Assert: m.[[Status]] is evaluating-async.
   2250      MOZ_ASSERT(m->status() == ModuleStatus::EvaluatingAsync);
   2251 
   2252      // Step 1.a.ii. Assert: m.[[EvaluationError]] is empty.
   2253      MOZ_ASSERT(!m->hadEvaluationError());
   2254 
   2255      // Step 1.a.iii. Assert: m.[[AsyncEvaluationOrder]] is an integer.
   2256      MOZ_ASSERT(m->asyncEvaluationOrder().isInteger());
   2257 
   2258      // Step 1.a.iv. Assert: m.[[PendingAsyncDependencies]] > 0.
   2259      MOZ_ASSERT(m->pendingAsyncDependencies() > 0);
   2260 
   2261      // Step 1.a.v. Set m.[[PendingAsyncDependencies]] to
   2262      // m.[[PendingAsyncDependencies]] - 1.
   2263      m->setPendingAsyncDependencies(m->pendingAsyncDependencies() - 1);
   2264 
   2265      // Step 1.a.vi. If m.[[PendingAsyncDependencies]] = 0, then:
   2266      if (m->pendingAsyncDependencies() == 0) {
   2267        // Step 1.a.vi.1. Append m to execList.
   2268        if (!execList.append(m)) {
   2269          return false;
   2270        }
   2271 
   2272        // Step 1.a.vi.2. If m.[[HasTLA]] is false, perform
   2273        //                GatherAvailableAncestors(m, execList).
   2274        if (!m->hasTopLevelAwait() &&
   2275            !GatherAvailableModuleAncestors(cx, m, execList)) {
   2276          return false;
   2277        }
   2278      }
   2279    }
   2280  }
   2281 
   2282  // Step 2. Return unused.
   2283  return true;
   2284 }
   2285 
   2286 struct EvalOrderComparator {
   2287  bool operator()(ModuleObject* a, ModuleObject* b, bool* lessOrEqualp) {
   2288    int32_t result = int32_t(a->asyncEvaluationOrder().get()) -
   2289                     int32_t(b->asyncEvaluationOrder().get());
   2290    *lessOrEqualp = (result <= 0);
   2291    return true;
   2292  }
   2293 };
   2294 
   2295 static void RejectExecutionWithPendingException(JSContext* cx,
   2296                                                Handle<ModuleObject*> module) {
   2297  // If there is no exception pending then we have been interrupted or have
   2298  // OOM'd and all bets are off. We reject the execution by throwing
   2299  // undefined. Not much more we can do.
   2300  RootedValue exception(cx);
   2301  if (cx->isExceptionPending()) {
   2302    (void)cx->getPendingException(&exception);
   2303  }
   2304  cx->clearPendingException();
   2305  AsyncModuleExecutionRejected(cx, module, exception);
   2306 }
   2307 
   2308 // https://tc39.es/ecma262/#sec-async-module-execution-fulfilled
   2309 // ES2023 16.2.1.5.2.4 AsyncModuleExecutionFulfilled
   2310 void js::AsyncModuleExecutionFulfilled(JSContext* cx,
   2311                                       Handle<ModuleObject*> module) {
   2312  // Step 1. If module.[[Status]] is evaluated, then:
   2313  if (module->status() == ModuleStatus::Evaluated) {
   2314    // Step 1.a. Assert: module.[[EvaluationError]] is not empty.
   2315    MOZ_ASSERT(module->hadEvaluationError());
   2316 
   2317    // Step 1.b. Return unused.
   2318    return;
   2319  }
   2320 
   2321  // Step 2. Assert: module.[[Status]] is evaluating-async.
   2322  MOZ_ASSERT(module->status() == ModuleStatus::EvaluatingAsync);
   2323 
   2324  // Step 3. Assert: module.[[AsyncEvaluationOrder]] is an integer.
   2325  MOZ_ASSERT(module->asyncEvaluationOrder().isInteger());
   2326 
   2327  // Step 4. Assert: module.[[EvaluationError]] is empty.
   2328  MOZ_ASSERT(!module->hadEvaluationError());
   2329 
   2330  // The following steps are performed in a different order from the
   2331  // spec. Gather available module ancestors before mutating the module object
   2332  // as this can fail in our implementation.
   2333 
   2334  // Step 8. Let execList be a new empty List.
   2335  Rooted<ModuleVector> execList(cx);
   2336 
   2337  // Step 9. Perform GatherAvailableAncestors(module, execList).
   2338  if (!GatherAvailableModuleAncestors(cx, module, &execList)) {
   2339    RejectExecutionWithPendingException(cx, module);
   2340    return;
   2341  }
   2342 
   2343  // Step 10. Let sortedExecList be a List whose elements are the elements of
   2344  //          execList, in the order in which they had their [[AsyncEvaluation]]
   2345  //          fields set to true in InnerModuleEvaluation.
   2346 
   2347  Rooted<ModuleVector> scratch(cx);
   2348  if (!scratch.resize(execList.length())) {
   2349    ReportOutOfMemory(cx);
   2350    RejectExecutionWithPendingException(cx, module);
   2351    return;
   2352  }
   2353 
   2354  MOZ_ALWAYS_TRUE(MergeSort(execList.begin(), execList.length(),
   2355                            scratch.begin(), EvalOrderComparator()));
   2356 
   2357  // Step 11. Assert: All elements of sortedExecList have their
   2358  //          [[AsyncEvaluationOrder]] field set to an integer,
   2359  //          [[PendingAsyncDependencies]] field set to 0, and
   2360  //          [[EvaluationError]] field set to empty.
   2361 #ifdef DEBUG
   2362  for (ModuleObject* m : execList) {
   2363    MOZ_ASSERT(m->asyncEvaluationOrder().isInteger());
   2364    MOZ_ASSERT(m->pendingAsyncDependencies() == 0);
   2365    MOZ_ASSERT(!m->hadEvaluationError());
   2366  }
   2367 #endif
   2368 
   2369  // Return to original order of steps.
   2370 
   2371  ModuleObject::onTopLevelEvaluationFinished(module);
   2372 
   2373  // Step 5. Set module.[[AsyncEvaluationOrder]] to done.
   2374  module->asyncEvaluationOrder().setDone(cx->runtime());
   2375 
   2376  // Step 6. Set module.[[Status]] to evaluated.
   2377  module->setStatus(ModuleStatus::Evaluated);
   2378 
   2379  // Step 7. If module.[[TopLevelCapability]] is not empty, then:
   2380  if (module->hasTopLevelCapability()) {
   2381    // Step 7.a. Assert: module.[[CycleRoot]] is module.
   2382    MOZ_ASSERT(module->getCycleRoot() == module);
   2383 
   2384    // Step 7.b. Perform ! Call(module.[[TopLevelCapability]].[[Resolve]],
   2385    //           undefined, undefined).
   2386    if (!ModuleObject::topLevelCapabilityResolve(cx, module)) {
   2387      // If Resolve fails, there's nothing more we can do here.
   2388      cx->clearPendingException();
   2389    }
   2390  }
   2391 
   2392  // Step 12. For each Cyclic Module Record m of sortedExecList, do:
   2393  Rooted<ModuleObject*> m(cx);
   2394  for (ModuleObject* obj : execList) {
   2395    m = obj;
   2396 
   2397    // Step 12.a. If m.[[Status]] is evaluated, then:
   2398    if (m->status() == ModuleStatus::Evaluated) {
   2399      // Step 12.a.i. Assert: m.[[EvaluationError]] is not empty.
   2400      MOZ_ASSERT(m->hadEvaluationError());
   2401    } else if (m->hasTopLevelAwait()) {
   2402      // Step 12.b. Else if m.[[HasTLA]] is true, then:
   2403      // Step 12.b.i. Perform ExecuteAsyncModule(m).
   2404      if (!ExecuteAsyncModule(cx, m)) {
   2405        MOZ_ASSERT(cx->isThrowingOutOfMemory() || cx->isThrowingOverRecursed());
   2406        cx->clearPendingException();
   2407      }
   2408    } else {
   2409      // Step 12.c. Else:
   2410      // Step 12.c.i. Let result be m.ExecuteModule().
   2411      bool ok = ModuleObject::execute(cx, m);
   2412 
   2413      // Step 12.c.ii. If result is an abrupt completion, then:
   2414      if (!ok) {
   2415        // Step 12.c.ii.1. Perform AsyncModuleExecutionRejected(m,
   2416        //                 result.[[Value]]).
   2417        RejectExecutionWithPendingException(cx, m);
   2418      } else {
   2419        // Step 12.c.iii. Else:
   2420        // Step 12.c.iii.1. Set m.[[AsyncEvaluationOrder]] to done.
   2421        m->asyncEvaluationOrder().setDone(m->zone()->runtimeFromMainThread());
   2422        // Step 12.c.iii.2. Set m.[[Status]] to evaluated.
   2423        m->setStatus(ModuleStatus::Evaluated);
   2424 
   2425        // Step 12.c.iii.2. If m.[[TopLevelCapability]] is not empty, then:
   2426        if (m->hasTopLevelCapability()) {
   2427          // Step 12.c.iii.2.a. Assert: m.[[CycleRoot]] is m.
   2428          MOZ_ASSERT(m->getCycleRoot() == m);
   2429 
   2430          // Step 12.c.iii.2.b. Perform !
   2431          //                    Call(m.[[TopLevelCapability]].[[Resolve]],
   2432          //                    undefined, undefined).
   2433          if (!ModuleObject::topLevelCapabilityResolve(cx, m)) {
   2434            // If Resolve fails, there's nothing more we can do here.
   2435            cx->clearPendingException();
   2436          }
   2437        }
   2438      }
   2439    }
   2440  }
   2441 
   2442  // Step 13. Return unused.
   2443 }
   2444 
   2445 // https://tc39.es/ecma262/#sec-async-module-execution-rejected
   2446 // ES2023 16.2.1.5.2.5 AsyncModuleExecutionRejected
   2447 void js::AsyncModuleExecutionRejected(JSContext* cx,
   2448                                      Handle<ModuleObject*> module,
   2449                                      HandleValue error) {
   2450  // Step 1. If module.[[Status]] is evaluated, then:
   2451  if (module->status() == ModuleStatus::Evaluated) {
   2452    // Step 1.a. Assert: module.[[EvaluationError]] is not empty
   2453    MOZ_ASSERT(module->hadEvaluationError());
   2454 
   2455    // Step 1.b. Return unused.
   2456    return;
   2457  }
   2458 
   2459  // Step 2. Assert: module.[[Status]] is evaluating-async.
   2460  MOZ_ASSERT(module->status() == ModuleStatus::EvaluatingAsync);
   2461 
   2462  // Step 3. Assert: module.[[AsyncEvaluationOrder]] is an integer.
   2463  MOZ_ASSERT(module->asyncEvaluationOrder().isInteger());
   2464 
   2465  // Step 4. Assert: module.[[EvaluationError]] is empty.
   2466  MOZ_ASSERT(!module->hadEvaluationError());
   2467 
   2468  ModuleObject::onTopLevelEvaluationFinished(module);
   2469 
   2470  // Step 5. Set module.[[EvaluationError]] to ThrowCompletion(error).
   2471  module->setEvaluationError(error);
   2472 
   2473  // Step 6. Set module.[[Status]] to evaluated.
   2474  MOZ_ASSERT(module->status() == ModuleStatus::Evaluated);
   2475 
   2476  // Step 7. Set module.[[AsyncEvaluationOrder]] to done.
   2477  module->asyncEvaluationOrder().setDone(cx->runtime());
   2478 
   2479  // Step 9. If module.[[TopLevelCapability]] is not empty, then:
   2480  if (module->hasTopLevelCapability()) {
   2481    // Step 9.a. Assert: module.[[CycleRoot]] is module.
   2482    MOZ_ASSERT(module->getCycleRoot() == module);
   2483 
   2484    // Step 9.b. Perform ! Call(module.[[TopLevelCapability]].[[Reject]],
   2485    //           undefined, error).
   2486    if (!ModuleObject::topLevelCapabilityReject(cx, module, error)) {
   2487      // If Reject fails, there's nothing more we can do here.
   2488      cx->clearPendingException();
   2489    }
   2490  }
   2491 
   2492  // Step 10. For each Cyclic Module Record m of module.[[AsyncParentModules]],
   2493  //         do:
   2494  Rooted<ListObject*> parents(cx, module->asyncParentModules());
   2495  Rooted<ModuleObject*> parent(cx);
   2496  for (uint32_t i = 0; i < parents->length(); i++) {
   2497    parent = &parents->get(i).toObject().as<ModuleObject>();
   2498 
   2499    // Step 10.a. Perform AsyncModuleExecutionRejected(m, error).
   2500    AsyncModuleExecutionRejected(cx, parent, error);
   2501  }
   2502 
   2503  // Step 11. Return unused.
   2504 }
   2505 
   2506 // https://tc39.es/proposal-import-attributes/#sec-evaluate-import-call
   2507 // NOTE: The caller needs to handle the promise.
   2508 static bool EvaluateDynamicImportOptions(
   2509    JSContext* cx, HandleValue optionsArg,
   2510    MutableHandle<ImportAttributeVector> attributesArrayArg) {
   2511  // Step 11. If options is not undefined, then
   2512  if (optionsArg.isUndefined()) {
   2513    return true;
   2514  }
   2515 
   2516  // Step 11.a. If options is not an Object, then
   2517  if (!optionsArg.isObject()) {
   2518    JS_ReportErrorNumberASCII(
   2519        cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, "import",
   2520        "object or undefined", InformalValueTypeName(optionsArg));
   2521    return false;
   2522  }
   2523 
   2524  RootedObject attributesWrapperObject(cx, &optionsArg.toObject());
   2525  RootedValue attributesValue(cx);
   2526 
   2527  // Step 11.b. Let attributesObj be Completion(Get(options, "with")).
   2528  RootedId withId(cx, NameToId(cx->names().with));
   2529  if (!GetProperty(cx, attributesWrapperObject, attributesWrapperObject, withId,
   2530                   &attributesValue)) {
   2531    return false;
   2532  }
   2533 
   2534  // Step 11.e. If attributesObj is not undefined, then
   2535  if (attributesValue.isUndefined()) {
   2536    return true;
   2537  }
   2538 
   2539  // Step 11.e.i. If attributesObj is not an Object, then
   2540  if (!attributesValue.isObject()) {
   2541    JS_ReportErrorNumberASCII(
   2542        cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, "import",
   2543        "object or undefined", InformalValueTypeName(attributesValue));
   2544    return false;
   2545  }
   2546 
   2547  // Step 11.e.ii. Let entries be
   2548  // Completion(EnumerableOwnProperties(attributesObj, key+value)).
   2549  RootedObject attributesObject(cx, &attributesValue.toObject());
   2550  RootedIdVector attributes(cx);
   2551  if (!GetPropertyKeys(cx, attributesObject, JSITER_OWNONLY, &attributes)) {
   2552    return false;
   2553  }
   2554 
   2555  uint32_t numberOfAttributes = attributes.length();
   2556  if (numberOfAttributes == 0) {
   2557    return true;
   2558  }
   2559 
   2560  // Step 10 (reordered). Let attributes be a new empty List.
   2561  if (!attributesArrayArg.reserve(numberOfAttributes)) {
   2562    ReportOutOfMemory(cx);
   2563    return false;
   2564  }
   2565 
   2566  size_t numberOfValidAttributes = 0;
   2567 
   2568  // Step 11.e.iv. For each element entry of entries, do
   2569  RootedId key(cx);
   2570  RootedValue value(cx);
   2571  Rooted<JSAtom*> keyAtom(cx);
   2572  Rooted<JSString*> valueString(cx);
   2573  for (size_t i = 0; i < numberOfAttributes; i++) {
   2574    // Step 11.e.ii.iv.1. Let key be ! Get(entry, "0").
   2575    key = attributes[i];
   2576 
   2577    // Step 11.e.ii.iv.2. Let value be ! Get(entry, "1").
   2578    if (!GetProperty(cx, attributesObject, attributesObject, key, &value)) {
   2579      return false;
   2580    }
   2581 
   2582    // Step 11.e.ii.iv.3. If key is a String, then
   2583    if (key.isString()) {
   2584      // Step 11.f (reordered). If AllImportAttributesSupported(attributes) is
   2585      // false, then
   2586      //
   2587      // Note: This should be driven by a host hook
   2588      // (HostGetSupportedImportAttributes), however the infrastructure of said
   2589      // host hook is deeply unclear, and so right now embedders will not have
   2590      // the ability to alter or extend the set of supported attributes.
   2591      // See https://bugzilla.mozilla.org/show_bug.cgi?id=1840723.
   2592      bool supported = key.isAtom(cx->names().type);
   2593      if (!supported) {
   2594        UniqueChars printableKey = AtomToPrintableString(cx, key.toAtom());
   2595        if (!printableKey) {
   2596          return false;
   2597        }
   2598        JS_ReportErrorNumberASCII(
   2599            cx, GetErrorMessage, nullptr,
   2600            JSMSG_IMPORT_ATTRIBUTES_DYNAMIC_IMPORT_UNSUPPORTED_ATTRIBUTE,
   2601            printableKey.get());
   2602        return false;
   2603      }
   2604 
   2605      // Step 10.d.v.3.a. If value is not a String, then
   2606      if (!value.isString()) {
   2607        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2608                                  JSMSG_NOT_EXPECTED_TYPE, "import", "string",
   2609                                  InformalValueTypeName(value));
   2610        return false;
   2611      }
   2612 
   2613      // Step 10.d.v.3.b. Append the ImportAttribute Record { [[Key]]: key,
   2614      // [[Value]]: value } to attributes.
   2615      keyAtom = key.toAtom();
   2616      valueString = value.toString();
   2617      attributesArrayArg.infallibleEmplaceBack(keyAtom, valueString);
   2618      ++numberOfValidAttributes;
   2619    }
   2620  }
   2621 
   2622  if (numberOfValidAttributes == 0) {
   2623    return true;
   2624  }
   2625 
   2626  // Step 10.g (skipped). Sort attributes according to the lexicographic order
   2627  // of their [[Key]] fields, treating the value of each such field as a
   2628  // sequence of UTF-16 code unit values.
   2629  //
   2630  // We only support "type", so we can ignore this.
   2631 
   2632  return true;
   2633 }
   2634 
   2635 // https://tc39.es/ecma262/#sec-evaluate-import-call
   2636 //
   2637 // ShadowRealmImportValue duplicates some of this, so be sure to keep these in
   2638 // sync.
   2639 JSObject* js::StartDynamicModuleImport(JSContext* cx, HandleScript script,
   2640                                       HandleValue specifierArg,
   2641                                       HandleValue optionsArg) {
   2642  // Step 7. Let promiseCapability be ! NewPromiseCapability(%Promise%).
   2643  RootedObject promise(cx, JS::NewPromiseObject(cx, nullptr));
   2644  if (!promise) {
   2645    return nullptr;
   2646  }
   2647 
   2648  if (!TryStartDynamicModuleImport(cx, script, specifierArg, optionsArg,
   2649                                   promise)) {
   2650    if (!RejectPromiseWithPendingError(cx, promise.as<PromiseObject>())) {
   2651      return nullptr;
   2652    }
   2653  }
   2654 
   2655  return promise;
   2656 }
   2657 
   2658 // https://tc39.es/ecma262/#sec-evaluate-import-call continued.
   2659 static bool TryStartDynamicModuleImport(JSContext* cx, HandleScript script,
   2660                                        HandleValue specifierArg,
   2661                                        HandleValue optionsArg,
   2662                                        HandleObject promise) {
   2663  RootedString specifier(cx, ToString(cx, specifierArg));
   2664  if (!specifier) {
   2665    return false;
   2666  }
   2667 
   2668  Rooted<JSAtom*> specifierAtom(cx, AtomizeString(cx, specifier));
   2669  if (!specifierAtom) {
   2670    return false;
   2671  }
   2672 
   2673  Rooted<ImportAttributeVector> attributes(cx);
   2674  if (!EvaluateDynamicImportOptions(cx, optionsArg, &attributes)) {
   2675    return false;
   2676  }
   2677 
   2678  // Step 12. Let moduleRequest be a new ModuleRequest Record { [[Specifier]]:
   2679  //          specifierString, [[Attributes]]: attributes }.
   2680  RootedObject moduleRequest(
   2681      cx, ModuleRequestObject::create(cx, specifierAtom, attributes,
   2682                                      ImportPhase::Evaluation));
   2683  if (!moduleRequest) {
   2684    return false;
   2685  }
   2686 
   2687  // Step 13. Perform HostLoadImportedModule(referrer, moduleRequest, empty,
   2688  //          promiseCapability).
   2689  RootedValue payload(cx, ObjectValue(*promise));
   2690  (void)HostLoadImportedModule(cx, script, moduleRequest,
   2691                               JS::UndefinedHandleValue, payload);
   2692 
   2693  return true;
   2694 }
   2695 
   2696 static bool OnRootModuleRejected(JSContext* cx, unsigned argc, Value* vp) {
   2697  CallArgs args = CallArgsFromVp(argc, vp);
   2698  HandleValue error = args.get(0);
   2699 
   2700  ReportExceptionClosure reportExn(error);
   2701  PrepareScriptEnvironmentAndInvoke(cx, cx->global(), reportExn);
   2702 
   2703  args.rval().setUndefined();
   2704  return true;
   2705 };
   2706 
   2707 bool js::OnModuleEvaluationFailure(JSContext* cx,
   2708                                   HandleObject evaluationPromise,
   2709                                   JS::ModuleErrorBehaviour errorBehaviour) {
   2710  if (evaluationPromise == nullptr) {
   2711    return false;
   2712  }
   2713 
   2714  // To allow module evaluation to happen synchronously throw the error
   2715  // immediately. This assumes that any error will already have caused the
   2716  // promise to be rejected, and doesn't support top-level await.
   2717  if (errorBehaviour == JS::ThrowModuleErrorsSync) {
   2718    JS::PromiseState state = JS::GetPromiseState(evaluationPromise);
   2719    MOZ_DIAGNOSTIC_ASSERT(state == JS::PromiseState::Rejected ||
   2720                          state == JS::PromiseState::Fulfilled);
   2721 
   2722    JS::SetSettledPromiseIsHandled(cx, evaluationPromise);
   2723    if (state == JS::PromiseState::Fulfilled) {
   2724      return true;
   2725    }
   2726 
   2727    RootedValue error(cx, JS::GetPromiseResult(evaluationPromise));
   2728    JS_SetPendingException(cx, error);
   2729    return false;
   2730  }
   2731 
   2732  RootedFunction onRejected(
   2733      cx, NewHandler(cx, OnRootModuleRejected, evaluationPromise));
   2734  if (!onRejected) {
   2735    return false;
   2736  }
   2737 
   2738  return JS::AddPromiseReactions(cx, evaluationPromise, nullptr, onRejected);
   2739 }
   2740 
   2741 // This is used for |fulfilledClosure| and |rejectedClosure| in
   2742 // https://tc39.es/ecma262/#sec-ContinueDynamicImport
   2743 //
   2744 // It is used to marshal some arguments and pass them through to the promise
   2745 // resolve and reject callbacks. It holds a reference to the referencing private
   2746 // to keep it alive until it is needed.
   2747 //
   2748 // TODO: The |referrer| field is used to keep the importing script alive while
   2749 // the import operation is happening. It is possible that this is no longer
   2750 // required.
   2751 class DynamicImportContextObject : public NativeObject {
   2752 public:
   2753  enum { ReferrerSlot = 0, PromiseSlot, ModuleSlot, PhaseSlot, SlotCount };
   2754 
   2755  static const JSClass class_;
   2756 
   2757  [[nodiscard]] static DynamicImportContextObject* create(
   2758      JSContext* cx, Handle<JSScript*> referrer, Handle<PromiseObject*> promise,
   2759      Handle<ModuleObject*> module, ImportPhase phase);
   2760 
   2761  JSScript* referrer() const;
   2762  PromiseObject* promise() const;
   2763  ModuleObject* module() const;
   2764  ImportPhase phase() const;
   2765 
   2766  static void finalize(JS::GCContext* gcx, JSObject* obj);
   2767 };
   2768 
   2769 /* static */
   2770 const JSClass DynamicImportContextObject::class_ = {
   2771    "DynamicImportContextObject",
   2772    JSCLASS_HAS_RESERVED_SLOTS(DynamicImportContextObject::SlotCount)};
   2773 
   2774 /* static */
   2775 DynamicImportContextObject* DynamicImportContextObject::create(
   2776    JSContext* cx, Handle<JSScript*> referrer, Handle<PromiseObject*> promise,
   2777    Handle<ModuleObject*> module, ImportPhase phase) {
   2778  Rooted<DynamicImportContextObject*> self(
   2779      cx, NewObjectWithGivenProto<DynamicImportContextObject>(cx, nullptr));
   2780  if (!self) {
   2781    return nullptr;
   2782  }
   2783 
   2784  if (referrer) {
   2785    self->initReservedSlot(ReferrerSlot, PrivateGCThingValue(referrer));
   2786  }
   2787  self->initReservedSlot(PromiseSlot, ObjectValue(*promise));
   2788  self->initReservedSlot(ModuleSlot, ObjectValue(*module));
   2789  self->initReservedSlot(PhaseSlot, Int32Value(int32_t(phase)));
   2790  return self;
   2791 }
   2792 
   2793 JSScript* DynamicImportContextObject::referrer() const {
   2794  Value value = getReservedSlot(ReferrerSlot);
   2795  if (value.isUndefined()) {
   2796    return nullptr;
   2797  }
   2798 
   2799  return static_cast<JSScript*>(value.toGCThing());
   2800 }
   2801 
   2802 PromiseObject* DynamicImportContextObject::promise() const {
   2803  Value value = getReservedSlot(PromiseSlot);
   2804  if (value.isUndefined()) {
   2805    return nullptr;
   2806  }
   2807 
   2808  return &value.toObject().as<PromiseObject>();
   2809 }
   2810 
   2811 ModuleObject* DynamicImportContextObject::module() const {
   2812  Value value = getReservedSlot(ModuleSlot);
   2813  if (value.isUndefined()) {
   2814    return nullptr;
   2815  }
   2816 
   2817  return &value.toObject().as<ModuleObject>();
   2818 }
   2819 
   2820 ImportPhase DynamicImportContextObject::phase() const {
   2821  Value value = getReservedSlot(PhaseSlot);
   2822  if (value.isUndefined()) {
   2823    return ImportPhase::Limit;
   2824  }
   2825 
   2826  return static_cast<ImportPhase>(value.toInt32());
   2827 }
   2828 
   2829 // https://tc39.es/ecma262/#sec-ContinueDynamicImport
   2830 /* static */
   2831 bool ContinueDynamicImport(JSContext* cx, Handle<JSScript*> referrer,
   2832                           Handle<PromiseObject*> promiseCapability,
   2833                           Handle<ModuleObject*> module, ImportPhase phase,
   2834                           bool usePromise) {
   2835  MOZ_ASSERT(module);
   2836 
   2837  // Step 1, 2: Already handled in FinishLoadingImportedModuleFailed functions.
   2838 
   2839  // Step 6. Let linkAndEvaluateClosure be a new Abstract Closure with no
   2840  // parameters that captures module, promiseCapability, and onRejected...
   2841  Rooted<DynamicImportContextObject*> context(
   2842      cx, DynamicImportContextObject::create(cx, referrer, promiseCapability,
   2843                                             module, phase));
   2844  if (!context) {
   2845    return RejectPromiseWithPendingError(cx, promiseCapability);
   2846  }
   2847 
   2848  // Our implementation provides an option for synchronous completion for
   2849  // environments where we can't use promises.
   2850  if (!usePromise) {
   2851    return LinkAndEvaluateDynamicImport(cx, context);
   2852  }
   2853 
   2854  // Step 3: The module dependencies has been loaded in the host layer, so we
   2855  // only need to do _linkAndEvaluate_ part defined in the spec. Create a
   2856  // promise that we'll resolve immediately.
   2857  JS::Rooted<PromiseObject*> loadPromise(cx, CreatePromiseObjectForAsync(cx));
   2858  if (!loadPromise) {
   2859    return RejectPromiseWithPendingError(cx, promiseCapability);
   2860  }
   2861 
   2862  // Step 7. Let linkAndEvaluate be
   2863  //         CreateBuiltinFunction(linkAndEvaluateClosure, 0, "", []).
   2864  Rooted<JSFunction*> linkAndEvaluate(cx);
   2865  linkAndEvaluate = js::NewFunctionWithReserved(
   2866      cx, LinkAndEvaluateDynamicImport, 0, 0, "resolved");
   2867  if (!linkAndEvaluate) {
   2868    return RejectPromiseWithPendingError(cx, promiseCapability);
   2869  }
   2870 
   2871  // Step 8. Perform PerformPromiseThen(loadPromise, linkAndEvaluate,
   2872  // onRejected).
   2873  js::SetFunctionNativeReserved(linkAndEvaluate, 0, ObjectValue(*context));
   2874  JS::AddPromiseReactions(cx, loadPromise, linkAndEvaluate, nullptr);
   2875  return AsyncFunctionReturned(cx, loadPromise, UndefinedHandleValue);
   2876 }
   2877 
   2878 // static
   2879 bool LinkAndEvaluateDynamicImport(JSContext* cx, unsigned argc, Value* vp) {
   2880  CallArgs args = CallArgsFromVp(argc, vp);
   2881  Value value = js::GetFunctionNativeReserved(&args.callee(), 0);
   2882  Rooted<DynamicImportContextObject*> context(cx);
   2883  context = &value.toObject().as<DynamicImportContextObject>();
   2884  return LinkAndEvaluateDynamicImport(cx, context);
   2885 }
   2886 
   2887 // https://tc39.es/ecma262/#sec-ContinueDynamicImport
   2888 static bool LinkAndEvaluateDynamicImport(
   2889    JSContext* cx, Handle<DynamicImportContextObject*> context) {
   2890  MOZ_ASSERT(context);
   2891  Rooted<ModuleObject*> module(cx, context->module());
   2892  Rooted<PromiseObject*> promise(cx, context->promise());
   2893 
   2894  // Step 6.a. Let link be Completion(module.Link()).
   2895  if (!JS::ModuleLink(cx, module)) {
   2896    //   b. If link is an abrupt completion, then
   2897    //      i. Perform ! Call(promiseCapability.[[Reject]], undefined, [
   2898    //         link.[[Value]] ]).
   2899    //      ii. Return unused.
   2900    return RejectPromiseWithPendingError(cx, promise);
   2901  }
   2902  MOZ_ASSERT(!JS_IsExceptionPending(cx));
   2903 
   2904  // TODO: Bug 1952263: Implement Defer Imports Evaluation.
   2905  MOZ_ASSERT(context->phase() == ImportPhase::Evaluation);
   2906 
   2907  // Step 6.c. Let evaluatePromise be module.Evaluate().
   2908  JS::Rooted<JS::Value> rval(cx);
   2909  mozilla::DebugOnly<bool> ok = JS::ModuleEvaluate(cx, module, &rval);
   2910  MOZ_ASSERT_IF(ok, !JS_IsExceptionPending(cx));
   2911  if (!rval.isObject()) {
   2912    // If we do not have an evaluation promise or a module request for the
   2913    // module, we can assume that evaluation has failed or been interrupted and
   2914    // can reject the dynamic module.
   2915    return RejectPromiseWithPendingError(cx, promise);
   2916  }
   2917 
   2918  JS::Rooted<JSObject*> evaluatePromise(cx, &rval.toObject());
   2919  MOZ_ASSERT(evaluatePromise->is<PromiseObject>());
   2920 
   2921  // Step 6.e. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 0, "",
   2922  //           []).
   2923  RootedValue contextValue(cx, ObjectValue(*context));
   2924  RootedFunction onFulfilled(cx);
   2925  onFulfilled = NewHandlerWithExtraValue(cx, DynamicImportResolved, promise,
   2926                                         contextValue);
   2927  if (!onFulfilled) {
   2928    return false;
   2929  }
   2930 
   2931  // Step 5. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "",
   2932  //         []).
   2933  RootedFunction onRejected(cx);
   2934  onRejected = NewHandlerWithExtraValue(cx, DynamicImportRejected, promise,
   2935                                        contextValue);
   2936  if (!onRejected) {
   2937    return false;
   2938  }
   2939 
   2940  // Step 6.f. Perform PerformPromiseThen(evaluatePromise, onFulfilled,
   2941  //           onRejected).
   2942  // Step 6.g. Return unused.
   2943  return JS::AddPromiseReactionsIgnoringUnhandledRejection(
   2944      cx, evaluatePromise, onFulfilled, onRejected);
   2945 }
   2946 
   2947 // This performs the steps for |fulfilledClosure| from
   2948 // https://tc39.es/ecma262/#sec-ContinueDynamicImport step 6.d.
   2949 //
   2950 // With adjustment for Top-level await:
   2951 // https://GitHub.com/tc39/proposal-dynamic-import/pull/71/files
   2952 static bool DynamicImportResolved(JSContext* cx, unsigned argc, Value* vp) {
   2953  CallArgs args = CallArgsFromVp(argc, vp);
   2954  MOZ_ASSERT(args.get(0).isUndefined());
   2955 
   2956  Rooted<DynamicImportContextObject*> context(
   2957      cx, ExtraFromHandler<DynamicImportContextObject>(args));
   2958 
   2959  Rooted<PromiseObject*> promise(cx, TargetFromHandler<PromiseObject>(args));
   2960 
   2961  Rooted<ModuleObject*> module(cx, context->module());
   2962  if (module->status() != ModuleStatus::EvaluatingAsync &&
   2963      module->status() != ModuleStatus::Evaluated) {
   2964    JS_ReportErrorASCII(
   2965        cx, "Unevaluated or errored module returned by module resolve hook");
   2966    return RejectPromiseWithPendingError(cx, promise);
   2967  }
   2968 
   2969  // This is called when |evaluationPromise| is resolved, step 6.f.
   2970  MOZ_ASSERT_IF(module->hasCyclicModuleFields(),
   2971                module->getCycleRoot()
   2972                        ->topLevelCapability()
   2973                        ->as<PromiseObject>()
   2974                        .state() == JS::PromiseState::Fulfilled);
   2975 
   2976  // Step 6.d.i. Let namespace be GetModuleNamespace(module).
   2977  RootedObject ns(cx, GetOrCreateModuleNamespace(cx, module));
   2978  if (!ns) {
   2979    return RejectPromiseWithPendingError(cx, promise);
   2980  }
   2981 
   2982  // Step 6.d.ii. Perform ! Call(promiseCapability.[[Resolve]], undefined, [
   2983  //              namespace ]).
   2984  RootedValue value(cx, ObjectValue(*ns));
   2985  if (!PromiseObject::resolve(cx, promise, value)) {
   2986    return false;
   2987  }
   2988 
   2989  // Step 6.d.iii. Return NormalCompletion(undefined).
   2990  args.rval().setUndefined();
   2991  return true;
   2992 };
   2993 
   2994 // This performs the steps for |rejectedClosure| from
   2995 // https://tc39.es/ecma262/#sec-ContinueDynamicImport step 4.
   2996 static bool DynamicImportRejected(JSContext* cx, unsigned argc, Value* vp) {
   2997  CallArgs args = CallArgsFromVp(argc, vp);
   2998  HandleValue error = args.get(0);
   2999 
   3000  Rooted<DynamicImportContextObject*> context(
   3001      cx, ExtraFromHandler<DynamicImportContextObject>(args));
   3002 
   3003  Rooted<PromiseObject*> promise(cx, TargetFromHandler<PromiseObject>(args));
   3004 
   3005  // Step 4.a. Perform ! Call(promiseCapability.[[Reject]], undefined, [ reason
   3006  // ]).
   3007  if (!PromiseObject::reject(cx, promise, error)) {
   3008    return false;
   3009  }
   3010 
   3011  // Step 4.b. Return NormalCompletion(undefined).
   3012  args.rval().setUndefined();
   3013  return true;
   3014 }