diff options
Diffstat (limited to 'js/src/builtin/Module.js')
-rw-r--r-- | js/src/builtin/Module.js | 781 |
1 files changed, 781 insertions, 0 deletions
diff --git a/js/src/builtin/Module.js b/js/src/builtin/Module.js new file mode 100644 index 0000000000..a8a111e5af --- /dev/null +++ b/js/src/builtin/Module.js @@ -0,0 +1,781 @@ +/* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function CallModuleResolveHook(module, specifier, expectedMinimumStatus) +{ + let requestedModule = HostResolveImportedModule(module, specifier); + if (requestedModule.status < expectedMinimumStatus) + ThrowInternalError(JSMSG_BAD_MODULE_STATUS); + + return requestedModule; +} + +// https://tc39.es/ecma262/#sec-getexportednames +// ES2020 15.2.1.17.2 GetExportedNames +function ModuleGetExportedNames(exportStarSet = []) +{ + if (!IsObject(this) || !IsModule(this)) { + return callFunction(CallModuleMethodIfWrapped, this, exportStarSet, + "ModuleGetExportedNames"); + } + + // Step 3 + let module = this; + + // Step 4 + if (callFunction(ArrayIncludes, exportStarSet, module)) + return []; + + // Step 5 + _DefineDataProperty(exportStarSet, exportStarSet.length, module); + + // Step 6 + let exportedNames = []; + let namesCount = 0; + + // Step 7 + let localExportEntries = module.localExportEntries; + for (let i = 0; i < localExportEntries.length; i++) { + let e = localExportEntries[i]; + _DefineDataProperty(exportedNames, namesCount++, e.exportName); + } + + // Step 8 + let indirectExportEntries = module.indirectExportEntries; + for (let i = 0; i < indirectExportEntries.length; i++) { + let e = indirectExportEntries[i]; + _DefineDataProperty(exportedNames, namesCount++, e.exportName); + } + + // Step 9 + let starExportEntries = module.starExportEntries; + for (let i = 0; i < starExportEntries.length; i++) { + let e = starExportEntries[i]; + let requestedModule = CallModuleResolveHook(module, e.moduleRequest, + MODULE_STATUS_UNLINKED); + let starNames = callFunction(requestedModule.getExportedNames, requestedModule, + exportStarSet); + for (let j = 0; j < starNames.length; j++) { + let n = starNames[j]; + if (n !== "default" && !callFunction(ArrayIncludes, exportedNames, n)) + _DefineDataProperty(exportedNames, namesCount++, n); + } + } + + return exportedNames; +} + +function ModuleSetStatus(module, newStatus) +{ + assert(newStatus >= MODULE_STATUS_UNLINKED && + newStatus <= MODULE_STATUS_EVALUATED_ERROR, + "Bad new module status in ModuleSetStatus"); + + // Note that under OOM conditions we can fail the module instantiation + // process even after modules have been marked as instantiated. + assert((module.status <= MODULE_STATUS_LINKED && + newStatus === MODULE_STATUS_UNLINKED) || + newStatus > module.status, + "New module status inconsistent with current status"); + + UnsafeSetReservedSlot(module, MODULE_OBJECT_STATUS_SLOT, newStatus); +} + +// https://tc39.es/ecma262/#sec-getexportednames +// ES2020 15.2.1.17.3 ResolveExport +// +// Returns an object describing the location of the resolved export or +// indicating a failure. +// +// On success this returns a resolved binding record: { module, bindingName } +// +// There are two failure cases: +// +// - If no definition was found or the request is found to be circular, *null* +// is returned. +// +// - If the request is found to be ambiguous, the string `"ambiguous"` is +// returned. +// +function ModuleResolveExport(exportName, resolveSet = []) +{ + if (!IsObject(this) || !IsModule(this)) { + return callFunction(CallModuleMethodIfWrapped, this, exportName, resolveSet, + "ModuleResolveExport"); + } + + // Step 3 + let module = this; + + // Step 4 + for (let i = 0; i < resolveSet.length; i++) { + let r = resolveSet[i]; + if (r.module === module && r.exportName === exportName) { + // This is a circular import request. + return null; + } + } + + // Step 5 + _DefineDataProperty(resolveSet, resolveSet.length, {module, exportName}); + + // Step 6 + let localExportEntries = module.localExportEntries; + for (let i = 0; i < localExportEntries.length; i++) { + let e = localExportEntries[i]; + if (exportName === e.exportName) + return {module, bindingName: e.localName}; + } + + // Step 7 + let indirectExportEntries = module.indirectExportEntries; + for (let i = 0; i < indirectExportEntries.length; i++) { + let e = indirectExportEntries[i]; + if (exportName === e.exportName) { + let importedModule = CallModuleResolveHook(module, e.moduleRequest, + MODULE_STATUS_UNLINKED); + if (e.importName === "*") { + return {module: importedModule, bindingName: "*namespace*"}; + } + return callFunction(importedModule.resolveExport, importedModule, e.importName, + resolveSet); + } + } + + // Step 8 + if (exportName === "default") { + // A default export cannot be provided by an export *. + return null; + } + + // Step 9 + let starResolution = null; + + // Step 10 + let starExportEntries = module.starExportEntries; + for (let i = 0; i < starExportEntries.length; i++) { + let e = starExportEntries[i]; + let importedModule = CallModuleResolveHook(module, e.moduleRequest, + MODULE_STATUS_UNLINKED); + let resolution = callFunction(importedModule.resolveExport, importedModule, exportName, + resolveSet); + if (resolution === "ambiguous") + return resolution; + if (resolution !== null) { + if (starResolution === null) { + starResolution = resolution; + } else { + if (resolution.module !== starResolution.module || + resolution.bindingName !== starResolution.bindingName) + { + return "ambiguous"; + } + } + } + } + + // Step 11 + return starResolution; +} + +function IsResolvedBinding(resolution) +{ + assert(resolution === "ambiguous" || typeof resolution === "object", + "Bad module resolution result"); + return typeof resolution === "object" && resolution !== null; +} + +// https://tc39.es/ecma262/#sec-getmodulenamespace +// ES2020 15.2.1.21 GetModuleNamespace +function GetModuleNamespace(module) +{ + // Step 1 + assert(IsObject(module) && IsModule(module), "GetModuleNamespace called with non-module"); + + // Step 2 + assert(module.status !== MODULE_STATUS_UNLINKED, + "Bad module state in GetModuleNamespace"); + + // Step 3 + let namespace = module.namespace; + + // Step 4 + if (typeof namespace === "undefined") { + let exportedNames = callFunction(module.getExportedNames, module); + let unambiguousNames = []; + for (let i = 0; i < exportedNames.length; i++) { + let name = exportedNames[i]; + let resolution = callFunction(module.resolveExport, module, name); + if (IsResolvedBinding(resolution)) + _DefineDataProperty(unambiguousNames, unambiguousNames.length, name); + } + namespace = ModuleNamespaceCreate(module, unambiguousNames); + } + + // Step 5 + return namespace; +} + +// https://tc39.es/ecma262/#sec-modulenamespacecreate +// ES2020 9.4.6.11 ModuleNamespaceCreate +function ModuleNamespaceCreate(module, exports) +{ + callFunction(ArraySort, exports); + + let ns = NewModuleNamespace(module, exports); + + // Pre-compute all binding mappings now instead of on each access. + // See: ES2020 9.4.6.7 Module Namespace Exotic Object [[Get]] + for (let i = 0; i < exports.length; i++) { + let name = exports[i]; + let binding = callFunction(module.resolveExport, module, name); + assert(IsResolvedBinding(binding), "Failed to resolve binding"); + // ES2020 9.4.6.7 Module Namespace Exotic Object [[Get]], Step 10. + if (binding.bindingName === "*namespace*") { + let namespace = GetModuleNamespace(binding.module); + + // The spec uses an immutable binding here but we have already + // generated bytecode for an indirect binding. Instead, use an + // indirect binding to "*namespace*" slot of the target environment. + EnsureModuleEnvironmentNamespace(binding.module, namespace); + AddModuleNamespaceBinding(ns, name, binding.module, binding.bindingName); + } else { + AddModuleNamespaceBinding(ns, name, binding.module, binding.bindingName); + } + } + + return ns; +} + + +function GetModuleEnvironment(module) +{ + assert(IsObject(module) && IsModule(module), "Non-module passed to GetModuleEnvironment"); + + assert(module.status >= MODULE_STATUS_LINKING, + "Attempt to access module environement before linking"); + + let env = UnsafeGetReservedSlot(module, MODULE_OBJECT_ENVIRONMENT_SLOT); + assert(IsObject(env) && IsModuleEnvironment(env), + "Module environment slot contains unexpected value"); + + return env; +} + +function CountArrayValues(array, value) +{ + let count = 0; + for (let i = 0; i < array.length; i++) { + if (array[i] === value) + count++; + } + return count; +} + +function ArrayContains(array, value) +{ + for (let i = 0; i < array.length; i++) { + if (array[i] === value) + return true; + } + return false; +} + +function HandleModuleInstantiationFailure(module) +{ + // Reset the module to the "unlinked" state. Don't reset the + // environment slot as the environment object will be required by any + // possible future instantiation attempt. + ModuleSetStatus(module, MODULE_STATUS_UNLINKED); + UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, undefined); + UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, undefined); +} + +// 15.2.1.16.4 ModuleInstantiate() +function ModuleInstantiate() +{ + if (!IsObject(this) || !IsModule(this)) + return callFunction(CallModuleMethodIfWrapped, this, "ModuleInstantiate"); + + // Step 1 + let module = this; + + // Step 2 + if (module.status === MODULE_STATUS_LINKING || + module.status === MODULE_STATUS_EVALUATING) + { + ThrowInternalError(JSMSG_BAD_MODULE_STATUS); + } + + // Step 3 + let stack = []; + + // Steps 4-5 + try { + InnerModuleLinking(module, stack, 0); + } catch (error) { + for (let i = 0; i < stack.length; i++) { + let m = stack[i]; + if (m.status === MODULE_STATUS_LINKING) { + HandleModuleInstantiationFailure(m); + } + } + + // Handle OOM when appending to the stack or over-recursion errors. + if (stack.length === 0 && module.status === MODULE_STATUS_LINKING) { + HandleModuleInstantiationFailure(module); + } + + assert(module.status !== MODULE_STATUS_LINKING, + "Expected unlinked status after failed linking"); + + throw error; + } + + // Step 6 + assert(module.status === MODULE_STATUS_LINKED || + module.status === MODULE_STATUS_EVALUATED || + module.status === MODULE_STATUS_EVALUATED_ERROR, + "Bad module status after successful linking"); + + // Step 7 + assert(stack.length === 0, + "Stack should be empty after successful linking"); + + // Step 8 + return undefined; +} + +// https://tc39.es/ecma262/#sec-InnerModuleLinking +// ES2020 15.2.1.16.1.1 InnerModuleLinking +function InnerModuleLinking(module, stack, index) +{ + // Step 1 + // TODO: Support module records other than Cyclic Module Records. + // 1. If module is not a Cyclic Module Record, then + // a. Perform ? module.Link(). + // b. Return index. + + // Step 2 + if (module.status === MODULE_STATUS_LINKING || + module.status === MODULE_STATUS_LINKED || + module.status === MODULE_STATUS_EVALUATED || + module.status === MODULE_STATUS_EVALUATED_ERROR) + { + return index; + } + + // Step 3. Assert: module.[[Status]] is unlinked. + if (module.status !== MODULE_STATUS_UNLINKED) + ThrowInternalError(JSMSG_BAD_MODULE_STATUS); + + // Step 4. Set module.[[Status]] to linking. + ModuleSetStatus(module, MODULE_STATUS_LINKING); + + // Step 5. Set module.[[DFSIndex]] to index. + UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, index); + // Step 6. Set module.[[DFSAncestorIndex]] to index. + UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, index); + // Step 7. Set index to index + 1. + index++; + + // Step 8. Append module to stack. + _DefineDataProperty(stack, stack.length, module); + + // Step 9. For each String required that is an element of module.[[RequestedModules]], do + let requestedModules = module.requestedModules; + for (let i = 0; i < requestedModules.length; i++) { + // Step 9.a + let required = requestedModules[i].moduleSpecifier; + let requiredModule = CallModuleResolveHook(module, required, MODULE_STATUS_UNLINKED); + + // Step 9.b + index = InnerModuleLinking(requiredModule, stack, index); + + // TODO: Check if requiredModule is a Cyclic Module Record + // Step 9.c.i + assert(requiredModule.status === MODULE_STATUS_LINKING || + requiredModule.status === MODULE_STATUS_LINKED || + requiredModule.status === MODULE_STATUS_EVALUATED || + requiredModule.status === MODULE_STATUS_EVALUATED_ERROR, + "Bad required module status after InnerModuleLinking"); + + // Step 9.c.ii + assert((requiredModule.status === MODULE_STATUS_LINKING) === + ArrayContains(stack, requiredModule), + "Required module should be in the stack iff it is currently being instantiated"); + + assert(typeof requiredModule.dfsIndex === "number", "Bad dfsIndex"); + assert(typeof requiredModule.dfsAncestorIndex === "number", "Bad dfsAncestorIndex"); + + // Step 9.c.iii + if (requiredModule.status === MODULE_STATUS_LINKING) { + UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, + std_Math_min(module.dfsAncestorIndex, + requiredModule.dfsAncestorIndex)); + } + } + + // Step 10 + callFunction(InitializeEnvironment, module); + + // Step 11 + assert(CountArrayValues(stack, module) === 1, + "Current module should appear exactly once in the stack"); + // Step 12 + assert(module.dfsAncestorIndex <= module.dfsIndex, + "Bad DFS ancestor index"); + + // Step 13 + if (module.dfsAncestorIndex === module.dfsIndex) { + let requiredModule; + do { + // 13.b.i-ii + requiredModule = callFunction(std_Array_pop, stack); + // TODO: 13.b.ii. Assert: requiredModule is a Cyclic Module Record. + // Step 13.b.iv + ModuleSetStatus(requiredModule, MODULE_STATUS_LINKED); + } while (requiredModule !== module); + } + + // Step 14 + return index; +} + +// https://tc39.es/ecma262/#sec-source-text-module-record-initialize-environment +// ES2020 15.2.1.17.4 InitializeEnvironment +function InitializeEnvironment() +{ + // Step 1 + let module = this; + + // Step 2-3 + let indirectExportEntries = module.indirectExportEntries; + for (let i = 0; i < indirectExportEntries.length; i++) { + let e = indirectExportEntries[i]; + let resolution = callFunction(module.resolveExport, module, e.exportName); + if (!IsResolvedBinding(resolution)) { + ThrowResolutionError(module, resolution, "indirectExport", e.exportName, + e.lineNumber, e.columnNumber); + } + } + + // Omitting steps 4-5, for practical purposes it is impossible for a realm to be + // undefined at this point. + + // Step 6-8 + // Note that we have already created the environment by this point. + let env = GetModuleEnvironment(module); + + // Step 9 + let importEntries = module.importEntries; + for (let i = 0; i < importEntries.length; i++) { + let imp = importEntries[i]; + let importedModule = CallModuleResolveHook(module, imp.moduleRequest, + MODULE_STATUS_LINKING); + // Step 9.c-9.d + if (imp.importName === "*") { + let namespace = GetModuleNamespace(importedModule); + CreateNamespaceBinding(env, imp.localName, namespace); + } else { + let resolution = callFunction(importedModule.resolveExport, importedModule, + imp.importName); + if (!IsResolvedBinding(resolution)) { + ThrowResolutionError(module, resolution, "import", imp.importName, + imp.lineNumber, imp.columnNumber); + } + + if (resolution.bindingName === "*namespace*") { + let namespace = GetModuleNamespace(resolution.module); + + // This should be CreateNamespaceBinding, but we have already + // generated bytecode assuming an indirect binding. Instead, + // ensure a special "*namespace*"" binding exists on the target + // module's environment. We then generate an indirect binding to + // this synthetic binding. + EnsureModuleEnvironmentNamespace(resolution.module, namespace); + CreateImportBinding(env, imp.localName, resolution.module, + resolution.bindingName); + } else { + CreateImportBinding(env, imp.localName, resolution.module, + resolution.bindingName); + } + } + } + + // Steps 10-26 + // Some of these do not need to happen for practical purposes. For steps 21-23, the bindings + // that can be handled in a similar way to regulars scripts are done separately. Function + // Declarations are special due to hoisting and are handled within this function. + // See ModuleScope and ModuleEnvironmentObject for further details. + + // Step 24.a.iii is handled here. + // In order to have the functions correctly hoisted we need to do this separately. + InstantiateModuleFunctionDeclarations(module); +} + +function ThrowResolutionError(module, resolution, kind, name, line, column) +{ + assert(module.status === MODULE_STATUS_LINKING, + "Unexpected module status in ThrowResolutionError"); + + assert(kind === "import" || kind === "indirectExport", + "Unexpected kind in ThrowResolutionError"); + + assert(line > 0, + "Line number should be present for all imports and indirect exports"); + + let ambiguous = resolution === "ambiguous"; + + let errorNumber; + if (kind === "import") + errorNumber = ambiguous ? JSMSG_AMBIGUOUS_IMPORT : JSMSG_MISSING_IMPORT; + else + errorNumber = ambiguous ? JSMSG_AMBIGUOUS_INDIRECT_EXPORT : JSMSG_MISSING_INDIRECT_EXPORT; + + let message = GetErrorMessage(errorNumber) + ": " + name; + let error = CreateModuleSyntaxError(module, line, column, message); + throw error; +} + +function GetModuleEvaluationError(module) +{ + assert(IsObject(module) && IsModule(module), + "Non-module passed to GetModuleEvaluationError"); + assert(module.status === MODULE_STATUS_EVALUATED_ERROR, + "Bad module status in GetModuleEvaluationError"); + return UnsafeGetReservedSlot(module, MODULE_OBJECT_EVALUATION_ERROR_SLOT); +} + +function RecordModuleEvaluationError(module, error) +{ + // Set the module's EvaluationError slot to indicate a failed module + // evaluation. + + assert(IsObject(module) && IsModule(module), + "Non-module passed to RecordModuleEvaluationError"); + + if (module.status === MODULE_STATUS_EVALUATED_ERROR) { + // It would be nice to assert that |error| is the same as the one we + // previously recorded, but that's not always true in the case of out of + // memory and over-recursion errors. + return; + } + + ModuleSetStatus(module, MODULE_STATUS_EVALUATED_ERROR); + UnsafeSetReservedSlot(module, MODULE_OBJECT_EVALUATION_ERROR_SLOT, error); +} + +// https://tc39.es/ecma262/#sec-moduleevaluation +// ES2020 15.2.1.16.2 ModuleEvaluate +function ModuleEvaluate() +{ + if (!IsObject(this) || !IsModule(this)) + return callFunction(CallModuleMethodIfWrapped, this, "ModuleEvaluate"); + + const isTopLevelAwaitEnabled = IsTopLevelAwaitEnabled(); + + // Step 2 + let module = this; + + // Step 3 + if (module.status !== MODULE_STATUS_LINKED && + module.status !== MODULE_STATUS_EVALUATED && + module.status !== MODULE_STATUS_EVALUATED_ERROR) + { + ThrowInternalError(JSMSG_BAD_MODULE_STATUS); + } + + let capability = undefined; + if (isTopLevelAwaitEnabled) { + // Top-level Await Step 4 + if (module.status === MODULE_STATUS_EVALUATED) { + module = GetAsyncCycleRoot(module); + } + + // Top-level Await Step 5 + if (module.topLevelCapability) { + return module.topLevelCapability; + } + + capability = CreateTopLevelCapability(module); + } + + // Step 4 + let stack = []; + + // Steps 5-6 + try { + InnerModuleEvaluation(module, stack, 0); + if (isTopLevelAwaitEnabled) { + if (!module.asyncEvaluating) { + ModuleTopLevelCapabilityResolve(module); + } + // Steps 7-8 + assert(module.status === MODULE_STATUS_EVALUATED, + "Bad module status after successful evaluation"); + assert(stack.length === 0, + "Stack should be empty after successful evaluation"); + } + } catch (error) { + for (let i = 0; i < stack.length; i++) { + let m = stack[i]; + assert(m.status === MODULE_STATUS_EVALUATING, + "Bad module status after failed evaluation"); + RecordModuleEvaluationError(m, error); + } + + // Handle OOM when appending to the stack or over-recursion errors. + if (stack.length === 0) + RecordModuleEvaluationError(module, error); + + assert(module.status === MODULE_STATUS_EVALUATED_ERROR, + "Bad module status after failed evaluation"); + + if (isTopLevelAwaitEnabled) { + ModuleTopLevelCapabilityReject(module, error); + } else { + throw error; + } + } + + // Step 9 + return capability; +} + +// https://tc39.es/ecma262/#sec-innermoduleevaluation +// ES2020 15.2.1.16.2.1 InnerModuleEvaluation +function InnerModuleEvaluation(module, stack, index) +{ + const isTopLevelAwaitEnabled = IsTopLevelAwaitEnabled(); + + // Step 1 + // TODO: Support module records other than Cyclic Module Records. + + // Step 2 + if (module.status === MODULE_STATUS_EVALUATED_ERROR) + throw GetModuleEvaluationError(module); + + if (module.status === MODULE_STATUS_EVALUATED) + return index; + + // Step 3 + if (module.status === MODULE_STATUS_EVALUATING) + return index; + + // Step 4 + assert(module.status === MODULE_STATUS_LINKED, + "Bad module status in InnerModuleEvaluation"); + + // Step 5 + ModuleSetStatus(module, MODULE_STATUS_EVALUATING); + + // Steps 6-8 + UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, index); + UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, index); + + if (isTopLevelAwaitEnabled) { + UnsafeSetReservedSlot(module, MODULE_OBJECT_PENDING_ASYNC_DEPENDENCIES_SLOT, 0); + } + + index++; + + // Step 9 + _DefineDataProperty(stack, stack.length, module); + + // Step 10 + let requestedModules = module.requestedModules; + for (let i = 0; i < requestedModules.length; i++) { + let required = requestedModules[i].moduleSpecifier; + let requiredModule = + CallModuleResolveHook(module, required, MODULE_STATUS_LINKED); + + index = InnerModuleEvaluation(requiredModule, stack, index); + + assert(requiredModule.status === MODULE_STATUS_EVALUATING || + requiredModule.status === MODULE_STATUS_EVALUATED, + "Bad module status after InnerModuleEvaluation"); + + assert((requiredModule.status === MODULE_STATUS_EVALUATING) === + ArrayContains(stack, requiredModule), + "Required module should be in the stack iff it is currently being evaluated"); + + assert(typeof requiredModule.dfsIndex === "number", "Bad dfsIndex"); + assert(typeof requiredModule.dfsAncestorIndex === "number", "Bad dfsAncestorIndex"); + + if (requiredModule.status === MODULE_STATUS_EVALUATING) { + UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, + std_Math_min(module.dfsAncestorIndex, + requiredModule.dfsAncestorIndex)); + } else { + if (isTopLevelAwaitEnabled) { + requiredModule = GetAsyncCycleRoot(requiredModule); + assert(requiredModule.status === MODULE_STATUS_EVALUATED, + `Bad module status in InnerModuleEvaluation: ${requiredModule.status}`); + if (requiredModule.evaluationError) { + throw GetModuleEvaluationError(module); + } + } + } + if (isTopLevelAwaitEnabled) { + if (requiredModule.asyncEvaluating) { + UnsafeSetReservedSlot(module, + MODULE_OBJECT_PENDING_ASYNC_DEPENDENCIES_SLOT, + module.pendingAsyncDependencies + 1); + AppendAsyncParentModule(requiredModule, module); + } + } + } + + if (isTopLevelAwaitEnabled) { + if (module.pendingAsyncDependencies > 0) { + UnsafeSetReservedSlot(module, MODULE_OBJECT_ASYNC_EVALUATING_SLOT, true); + } else { + if (module.async) { + ExecuteAsyncModule(module); + } else { + // Step 11 + ExecuteModule(module); + } + } + } else { + // Step 11 + ExecuteModule(module); + } + + // Step 12 + assert(CountArrayValues(stack, module) === 1, + "Current module should appear exactly once in the stack"); + + // Step 13 + assert(module.dfsAncestorIndex <= module.dfsIndex, + "Bad DFS ancestor index"); + + // Step 14 + if (module.dfsAncestorIndex === module.dfsIndex) { + let requiredModule; + do { + requiredModule = callFunction(std_Array_pop, stack); + ModuleSetStatus(requiredModule, MODULE_STATUS_EVALUATED); + } while (requiredModule !== module); + } + + // Step 15 + return index; +} + +// https://tc39.es/proposal-top-level-await/#sec-execute-async-module +function ExecuteAsyncModule(module) { + // Steps 1-3. + assert(module.status == MODULE_STATUS_EVALUATING || + module.status == MODULE_STATUS_EVALUATED, "bad status for async module"); + assert(module.async, "module is not async"); + UnsafeSetReservedSlot(module, MODULE_OBJECT_ASYNC_EVALUATING_SLOT, true); + + // Step 4-11 done in AsyncAwait opcode + + ExecuteModule(module); +} + |