/* 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.state < expectedMinimumStatus) ThrowInternalError(JSMSG_BAD_MODULE_STATUS); return requestedModule; } // 15.2.1.16.2 GetExportedNames(exportStarSet) function ModuleGetExportedNames(exportStarSet = []) { if (!IsObject(this) || !IsModule(this)) { return callFunction(CallModuleMethodIfWrapped, this, exportStarSet, "ModuleGetExportedNames"); } // Step 1 let module = this; // Step 2 if (callFunction(ArrayIncludes, exportStarSet, module)) return []; // Step 3 _DefineDataProperty(exportStarSet, exportStarSet.length, module); // Step 4 let exportedNames = []; let namesCount = 0; // Step 5 let localExportEntries = module.localExportEntries; for (let i = 0; i < localExportEntries.length; i++) { let e = localExportEntries[i]; _DefineDataProperty(exportedNames, namesCount++, e.exportName); } // Step 6 let indirectExportEntries = module.indirectExportEntries; for (let i = 0; i < indirectExportEntries.length; i++) { let e = indirectExportEntries[i]; _DefineDataProperty(exportedNames, namesCount++, e.exportName); } // Step 7 let starExportEntries = module.starExportEntries; for (let i = 0; i < starExportEntries.length; i++) { let e = starExportEntries[i]; let requestedModule = CallModuleResolveHook(module, e.moduleRequest, MODULE_STATE_INSTANTIATED); 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_UNINSTANTIATED && 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_INSTANTIATED && newStatus === MODULE_STATUS_UNINSTANTIATED) || newStatus > module.status, "New module status inconsistent with current status"); UnsafeSetReservedSlot(module, MODULE_OBJECT_STATUS_SLOT, newStatus); } // 15.2.1.16.3 ResolveExport(exportName, resolveSet) // // 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 1 let module = this; // Step 2 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 3 _DefineDataProperty(resolveSet, resolveSet.length, {module: module, exportName: exportName}); // Step 4 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 5 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_UNINSTANTIATED); return callFunction(importedModule.resolveExport, importedModule, e.importName, resolveSet); } } // Step 6 if (exportName === "default") { // A default export cannot be provided by an export *. return null; } // Step 7 let starResolution = null; // Step 8 let starExportEntries = module.starExportEntries; for (let i = 0; i < starExportEntries.length; i++) { let e = starExportEntries[i]; let importedModule = CallModuleResolveHook(module, e.moduleRequest, MODULE_STATUS_UNINSTANTIATED); 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 9 return starResolution; } function IsResolvedBinding(resolution) { assert(resolution === "ambiguous" || typeof resolution === "object", "Bad module resolution result"); return typeof resolution === "object" && resolution !== null; } // 15.2.1.18 GetModuleNamespace(module) function GetModuleNamespace(module) { // Step 1 assert(IsModule(module), "GetModuleNamespace called with non-module"); // Steps 2-3 assert(module.status !== MODULE_STATUS_UNINSTANTIATED && module.status !== MODULE_STATUS_EVALUATED_ERROR, "Bad module status in GetModuleNamespace"); // Step 4 let namespace = module.namespace; 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 4 return namespace; } // 9.4.6.13 ModuleNamespaceCreate(module, exports) function ModuleNamespaceCreate(module, exports) { callFunction(std_Array_sort, exports); let ns = NewModuleNamespace(module, exports); // Pre-compute all bindings now rather than calling ResolveExport() on every // access. 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"); AddModuleNamespaceBinding(ns, name, binding.module, binding.bindingName); } return ns; } function GetModuleEnvironment(module) { assert(IsModule(module), "Non-module passed to GetModuleEnvironment"); assert(module.status >= MODULE_STATUS_INSTANTIATING, "Attempt to access module environement before instantation"); let env = UnsafeGetReservedSlot(module, MODULE_OBJECT_ENVIRONMENT_SLOT); assert(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 "uninstantiated" state. Don't reset the // environment slot as the environment object will be required by any // possible future instantiation attempt. ModuleSetStatus(module, MODULE_STATUS_UNINSTANTIATED); 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_INSTANTIATING || module.status === MODULE_STATUS_EVALUATING) { ThrowInternalError(JSMSG_BAD_MODULE_STATUS); } // Step 3 let stack = []; // Steps 4-5 try { InnerModuleInstantiation(module, stack, 0); } catch (error) { for (let i = 0; i < stack.length; i++) { let m = stack[i]; assert(m.status === MODULE_STATUS_INSTANTIATING, "Expected instantiating status during failed instantiation"); HandleModuleInstantiationFailure(m); } // Handle OOM when appending to the stack or over-recursion errors. if (stack.length === 0) HandleModuleInstantiationFailure(module); assert(module.status === MODULE_STATUS_UNINSTANTIATED, "Expected uninstantiated status after failed instantiation"); throw error; } // Step 6 assert(module.status === MODULE_STATUS_INSTANTIATED || module.status === MODULE_STATUS_EVALUATED || module.status === MODULE_STATUS_EVALUATED_ERROR, "Bad module status after successful instantiation"); // Step 7 assert(stack.length === 0, "Stack should be empty after successful instantiation"); // Step 8 return undefined; } _SetCanonicalName(ModuleInstantiate, "ModuleInstantiate"); // 15.2.1.16.4.1 InnerModuleInstantiation(module, stack, index) function InnerModuleInstantiation(module, stack, index) { // Step 1 // TODO: Support module records other than source text module records. // Step 2 if (module.status === MODULE_STATUS_INSTANTIATING || module.status === MODULE_STATUS_INSTANTIATED || module.status === MODULE_STATUS_EVALUATED || module.status === MODULE_STATUS_EVALUATED_ERROR) { return index; } // Step 3 assert(module.status === MODULE_STATUS_UNINSTANTIATED, "Bad module status in ModuleDeclarationInstantiation"); // Step 4 ModuleSetStatus(module, MODULE_STATUS_INSTANTIATING); // Steps 5-7 UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, index); UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, index); index++; // Step 8 _DefineDataProperty(stack, stack.length, module); // Step 9 let requestedModules = module.requestedModules; for (let i = 0; i < requestedModules.length; i++) { let required = requestedModules[i]; let requiredModule = CallModuleResolveHook(module, required, MODULE_STATUS_UNINSTANTIATED); index = InnerModuleInstantiation(requiredModule, stack, index); assert(requiredModule.status === MODULE_STATUS_INSTANTIATING || requiredModule.status === MODULE_STATUS_INSTANTIATED || requiredModule.status === MODULE_STATUS_EVALUATED || requiredModule.status === MODULE_STATUS_EVALUATED_ERROR, "Bad required module status after InnerModuleInstantiation"); assert((requiredModule.status === MODULE_STATUS_INSTANTIATING) === 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"); if (requiredModule.status === MODULE_STATUS_INSTANTIATING) { UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, std_Math_min(module.dfsAncestorIndex, requiredModule.dfsAncestorIndex)); } } // Step 10 ModuleDeclarationEnvironmentSetup(module); // Steps 11-12 assert(CountArrayValues(stack, module) === 1, "Current module should appear exactly once in the stack"); assert(module.dfsAncestorIndex <= module.dfsIndex, "Bad DFS ancestor index"); // Step 13 if (module.dfsAncestorIndex === module.dfsIndex) { let requiredModule; do { requiredModule = callFunction(std_Array_pop, stack); ModuleSetStatus(requiredModule, MODULE_STATUS_INSTANTIATED); } while (requiredModule !== module); } // Step 15 return index; } // 15.2.1.16.4.2 ModuleDeclarationEnvironmentSetup(module) function ModuleDeclarationEnvironmentSetup(module) { // Step 1 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); } } // Steps 5-6 // Note that we have already created the environment by this point. let env = GetModuleEnvironment(module); // Step 8 let importEntries = module.importEntries; for (let i = 0; i < importEntries.length; i++) { let imp = importEntries[i]; let importedModule = CallModuleResolveHook(module, imp.moduleRequest, MODULE_STATUS_INSTANTIATING); 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); } CreateImportBinding(env, imp.localName, resolution.module, resolution.bindingName); } } InstantiateModuleFunctionDeclarations(module); } function ThrowResolutionError(module, resolution, kind, name, line, column) { assert(module.status === MODULE_STATUS_INSTANTIATING, "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); } // 15.2.1.16.5 ModuleEvaluate() function ModuleEvaluate() { if (!IsObject(this) || !IsModule(this)) return callFunction(CallModuleMethodIfWrapped, this, "ModuleEvaluate"); // Step 1 let module = this; // Step 2 if (module.status !== MODULE_STATUS_INSTANTIATED && module.status !== MODULE_STATUS_EVALUATED && module.status !== MODULE_STATUS_EVALUATED_ERROR) { ThrowInternalError(JSMSG_BAD_MODULE_STATUS); } // Step 3 let stack = []; // Steps 4-5 try { InnerModuleEvaluation(module, stack, 0); } 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"); throw error; } assert(module.status === MODULE_STATUS_EVALUATED, "Bad module status after successful evaluation"); assert(stack.length === 0, "Stack should be empty after successful evaluation"); return undefined; } _SetCanonicalName(ModuleEvaluate, "ModuleEvaluate"); // 15.2.1.16.5.1 InnerModuleEvaluation(module, stack, index) function InnerModuleEvaluation(module, stack, index) { // Step 1 // TODO: Support module records other than source text 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_INSTANTIATED, "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); 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]; let requiredModule = CallModuleResolveHook(module, required, MODULE_STATUS_INSTANTIATED); 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)); } } // 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; }