diff options
Diffstat (limited to 'js/src/builtin/Module.js')
-rw-r--r-- | js/src/builtin/Module.js | 292 |
1 files changed, 140 insertions, 152 deletions
diff --git a/js/src/builtin/Module.js b/js/src/builtin/Module.js index 64d62d2160..86b44880dc 100644 --- a/js/src/builtin/Module.js +++ b/js/src/builtin/Module.js @@ -69,8 +69,13 @@ function ModuleSetStatus(module, newStatus) { assert(newStatus >= MODULE_STATUS_ERRORED && newStatus <= MODULE_STATUS_EVALUATED, "Bad new module status in ModuleSetStatus"); - if (newStatus !== MODULE_STATUS_ERRORED) - assert(newStatus > module.status, "New module status inconsistent with current status"); + + // 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); } @@ -80,20 +85,15 @@ function ModuleSetStatus(module, newStatus) // Returns an object describing the location of the resolved export or // indicating a failure. // -// On success this returns: { resolved: true, module, bindingName } -// -// There are three failure cases: +// On success this returns a resolved binding record: { module, bindingName } // -// - The resolution failure can be blamed on a particular module. -// Returns: { resolved: false, module, ambiguous: false } +// There are two failure cases: // -// - No culprit can be determined and the resolution failure was due to star -// export ambiguity. -// Returns: { resolved: false, module: null, ambiguous: true } +// - If no definition was found or the request is found to be circular, *null* +// is returned. // -// - No culprit can be determined and the resolution failure was not due to -// star export ambiguity. -// Returns: { resolved: false, module: null, ambiguous: false } +// - If the request is found to be ambiguous, the string `"ambiguous"` is +// returned. // function ModuleResolveExport(exportName, resolveSet = []) { @@ -106,53 +106,47 @@ function ModuleResolveExport(exportName, resolveSet = []) let module = this; // Step 2 - assert(module.status !== MODULE_STATUS_ERRORED, "Bad module status in ResolveExport"); - - // Step 3 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 {resolved: false, module: null, ambiguous: false}; + return null; } } - // Step 4 + // Step 3 _DefineDataProperty(resolveSet, resolveSet.length, {module: module, exportName: exportName}); - // Step 5 + // Step 4 let localExportEntries = module.localExportEntries; for (let i = 0; i < localExportEntries.length; i++) { let e = localExportEntries[i]; if (exportName === e.exportName) - return {resolved: true, module, bindingName: e.localName}; + return {module, bindingName: e.localName}; } - // Step 6 + // 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); - let resolution = callFunction(importedModule.resolveExport, importedModule, e.importName, - resolveSet); - if (!resolution.resolved && !resolution.module) - resolution.module = module; - return resolution; + return callFunction(importedModule.resolveExport, importedModule, e.importName, + resolveSet); } } - // Step 7 + // Step 6 if (exportName === "default") { // A default export cannot be provided by an export *. - return {resolved: false, module: null, ambiguous: false}; + return null; } - // Step 8 + // Step 7 let starResolution = null; - // Step 9 + // Step 8 let starExportEntries = module.starExportEntries; for (let i = 0; i < starExportEntries.length; i++) { let e = starExportEntries[i]; @@ -160,27 +154,31 @@ function ModuleResolveExport(exportName, resolveSet = []) MODULE_STATUS_UNINSTANTIATED); let resolution = callFunction(importedModule.resolveExport, importedModule, exportName, resolveSet); - if (!resolution.resolved && (resolution.module || resolution.ambiguous)) + if (resolution === "ambiguous") return resolution; - if (resolution.resolved) { + if (resolution !== null) { if (starResolution === null) { starResolution = resolution; } else { if (resolution.module !== starResolution.module || resolution.bindingName !== starResolution.bindingName) { - return {resolved: false, module: null, ambiguous: true}; + return "ambiguous"; } } } } - // Step 10 - if (starResolution !== null) - return starResolution; + // Step 9 + return starResolution; +} - return {resolved: false, module: null, ambiguous: false}; +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) @@ -189,12 +187,12 @@ function GetModuleNamespace(module) // Step 1 assert(IsModule(module), "GetModuleNamespace called with non-module"); - // Step 2 + // Steps 2-3 assert(module.status !== MODULE_STATUS_UNINSTANTIATED && - module.status !== MODULE_STATUS_ERRORED, + module.status !== MODULE_STATUS_EVALUATED_ERROR, "Bad module status in GetModuleNamespace"); - // Step 3 + // Step 4 let namespace = module.namespace; if (typeof namespace === "undefined") { @@ -203,7 +201,7 @@ function GetModuleNamespace(module) for (let i = 0; i < exportedNames.length; i++) { let name = exportedNames[i]; let resolution = callFunction(module.resolveExport, module, name); - if (resolution.resolved) + if (IsResolvedBinding(resolution)) _DefineDataProperty(unambiguousNames, unambiguousNames.length, name); } namespace = ModuleNamespaceCreate(module, unambiguousNames); @@ -225,7 +223,7 @@ function ModuleNamespaceCreate(module, exports) for (let i = 0; i < exports.length; i++) { let name = exports[i]; let binding = callFunction(module.resolveExport, module, name); - assert(binding.resolved, "Failed to resolve binding"); + assert(IsResolvedBinding(binding), "Failed to resolve binding"); AddModuleNamespaceBinding(ns, name, binding.module, binding.bindingName); } @@ -236,11 +234,6 @@ function GetModuleEnvironment(module) { assert(IsModule(module), "Non-module passed to GetModuleEnvironment"); - // Check for a previous failed attempt to instantiate this module. This can - // only happen due to a bug in the module loader. - if (module.status === MODULE_STATUS_ERRORED) - ThrowInternalError(JSMSG_MODULE_INSTANTIATE_FAILED, module.status); - let env = UnsafeGetReservedSlot(module, MODULE_OBJECT_ENVIRONMENT_SLOT); assert(env === undefined || IsModuleEnvironment(env), "Module environment slot contains unexpected value"); @@ -248,19 +241,6 @@ function GetModuleEnvironment(module) return env; } -function RecordModuleError(module, error) -{ - // Set the module's status to 'errored' to indicate a failed module - // instantiation and record the exception. The environment slot is also - // reset to 'undefined'. - - assert(IsObject(module) && IsModule(module), "Non-module passed to RecordModuleError"); - - ModuleSetStatus(module, MODULE_STATUS_ERRORED); - UnsafeSetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT, error); - UnsafeSetReservedSlot(module, MODULE_OBJECT_ENVIRONMENT_SLOT, undefined); -} - function CountArrayValues(array, value) { let count = 0; @@ -280,6 +260,16 @@ function ArrayContains(array, value) 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() { @@ -301,38 +291,30 @@ function ModuleInstantiate() // Steps 4-5 try { - InnerModuleDeclarationInstantiation(module, stack, 0); + InnerModuleInstantiation(module, stack, 0); } catch (error) { for (let i = 0; i < stack.length; i++) { let m = stack[i]; - assert(m.status === MODULE_STATUS_INSTANTIATING || - m.status === MODULE_STATUS_ERRORED, - "Bad module status after failed instantiation"); - - RecordModuleError(m, error); + assert(m.status === MODULE_STATUS_INSTANTIATING, + "Expected instantiating status during failed instantiation"); + HandleModuleInstantiationFailure(m); } - if (stack.length === 0 && - typeof(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT)) === "undefined") - { - // This can happen due to OOM when appending to the stack. - assert(error === "out of memory", - "Stack must contain module unless we hit OOM"); - RecordModuleError(module, error); - } + // Handle OOM when appending to the stack or over-recursion errors. + if (stack.length === 0) + HandleModuleInstantiationFailure(module); - assert(module.status === MODULE_STATUS_ERRORED, - "Bad module status after failed instantiation"); - assert(SameValue(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT), error), - "Module has different error set after failed instantiation"); + 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, + 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 @@ -344,8 +326,8 @@ function ModuleInstantiate() } _SetCanonicalName(ModuleInstantiate, "ModuleInstantiate"); -// 15.2.1.16.4.1 InnerModuleDeclarationInstantiation(module, stack, index) -function InnerModuleDeclarationInstantiation(module, stack, index) +// 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. @@ -353,42 +335,40 @@ function InnerModuleDeclarationInstantiation(module, stack, index) // Step 2 if (module.status === MODULE_STATUS_INSTANTIATING || module.status === MODULE_STATUS_INSTANTIATED || - module.status === MODULE_STATUS_EVALUATED) + module.status === MODULE_STATUS_EVALUATED || + module.status === MODULE_STATUS_EVALUATED_ERROR) { return index; } // Step 3 - if (module.status === MODULE_STATUS_ERRORED) - throw module.error; - - // Step 4 assert(module.status === MODULE_STATUS_UNINSTANTIATED, "Bad module status in ModuleDeclarationInstantiation"); - // Steps 5 + // Step 4 ModuleSetStatus(module, MODULE_STATUS_INSTANTIATING); - // Step 6-8 + // Steps 5-7 UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, index); UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, index); index++; - // Step 9 + // Step 8 _DefineDataProperty(stack, stack.length, module); - // Step 10 + // 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_ERRORED); + let requiredModule = CallModuleResolveHook(module, required, MODULE_STATUS_UNINSTANTIATED); - index = InnerModuleDeclarationInstantiation(requiredModule, stack, index); + index = InnerModuleInstantiation(requiredModule, stack, index); assert(requiredModule.status === MODULE_STATUS_INSTANTIATING || requiredModule.status === MODULE_STATUS_INSTANTIATED || - requiredModule.status === MODULE_STATUS_EVALUATED, - "Bad required module status after InnerModuleDeclarationInstantiation"); + 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), @@ -404,16 +384,16 @@ function InnerModuleDeclarationInstantiation(module, stack, index) } } - // Step 11 + // Step 10 ModuleDeclarationEnvironmentSetup(module); - // Steps 12-13 + // 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 14 + // Step 13 if (module.dfsAncestorIndex === module.dfsIndex) { let requiredModule; do { @@ -434,11 +414,9 @@ function ModuleDeclarationEnvironmentSetup(module) for (let i = 0; i < indirectExportEntries.length; i++) { let e = indirectExportEntries[i]; let resolution = callFunction(module.resolveExport, module, e.exportName); - assert(resolution.resolved || resolution.module, - "Unexpected failure to resolve export in ModuleDeclarationEnvironmentSetup"); - if (!resolution.resolved) { - return ResolutionError(resolution, "indirectExport", e.exportName, - e.lineNumber, e.columnNumber) + if (!IsResolvedBinding(resolution)) { + ThrowResolutionError(module, resolution, "indirectExport", e.exportName, + e.lineNumber, e.columnNumber); } } @@ -458,12 +436,9 @@ function ModuleDeclarationEnvironmentSetup(module) } else { let resolution = callFunction(importedModule.resolveExport, importedModule, imp.importName); - if (!resolution.resolved && !resolution.module) - resolution.module = module; - - if (!resolution.resolved) { - return ResolutionError(resolution, "import", imp.importName, - imp.lineNumber, imp.columnNumber); + if (!IsResolvedBinding(resolution)) { + ThrowResolutionError(module, resolution, "import", imp.importName, + imp.lineNumber, imp.columnNumber); } CreateImportBinding(env, imp.localName, resolution.module, resolution.bindingName); @@ -473,38 +448,58 @@ function ModuleDeclarationEnvironmentSetup(module) InstantiateModuleFunctionDeclarations(module); } -// 15.2.1.16.4.3 ResolutionError(module) -function ResolutionError(resolution, kind, name, line, column) +function ThrowResolutionError(module, resolution, kind, name, line, column) { - let module = resolution.module; - assert(module !== null, - "Null module passed to ResolutionError"); - - assert(module.status === MODULE_STATUS_UNINSTANTIATED || - module.status === MODULE_STATUS_INSTANTIATING, - "Unexpected module status in ResolutionError"); + assert(module.status === MODULE_STATUS_INSTANTIATING, + "Unexpected module status in ThrowResolutionError"); assert(kind === "import" || kind === "indirectExport", - "Unexpected kind in ResolutionError"); + "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 = resolution.ambiguous ? JSMSG_AMBIGUOUS_IMPORT - : JSMSG_MISSING_IMPORT; - } else { - errorNumber = resolution.ambiguous ? JSMSG_AMBIGUOUS_INDIRECT_EXPORT - : JSMSG_MISSING_INDIRECT_EXPORT; - } + 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); - RecordModuleError(module, error); 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() { @@ -515,9 +510,9 @@ function ModuleEvaluate() let module = this; // Step 2 - if (module.status !== MODULE_STATUS_ERRORED && - module.status !== MODULE_STATUS_INSTANTIATED && - module.status !== MODULE_STATUS_EVALUATED) + if (module.status !== MODULE_STATUS_INSTANTIATED && + module.status !== MODULE_STATUS_EVALUATED && + module.status !== MODULE_STATUS_EVALUATED_ERROR) { ThrowInternalError(JSMSG_BAD_MODULE_STATUS); } @@ -535,27 +530,20 @@ function ModuleEvaluate() assert(m.status === MODULE_STATUS_EVALUATING, "Bad module status after failed evaluation"); - RecordModuleError(m, error); + RecordModuleEvaluationError(m, error); } - if (stack.length === 0 && - typeof(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT)) === "undefined") - { - // This can happen due to OOM when appending to the stack. - assert(error === "out of memory", - "Stack must contain module unless we hit OOM"); - RecordModuleError(module, error); - } + // Handle OOM when appending to the stack or over-recursion errors. + if (stack.length === 0) + RecordModuleEvaluationError(module, error); - assert(module.status === MODULE_STATUS_ERRORED, + assert(module.status === MODULE_STATUS_EVALUATED_ERROR, "Bad module status after failed evaluation"); - assert(SameValue(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT), error), - "Module has different error set after failed evaluation"); throw error; } - assert(module.status == MODULE_STATUS_EVALUATED, + assert(module.status === MODULE_STATUS_EVALUATED, "Bad module status after successful evaluation"); assert(stack.length === 0, "Stack should be empty after successful evaluation"); @@ -571,19 +559,19 @@ function InnerModuleEvaluation(module, stack, index) // TODO: Support module records other than source text module records. // Step 2 - if (module.status === MODULE_STATUS_EVALUATING || - module.status === MODULE_STATUS_EVALUATED) - { + 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_ERRORED) - throw module.error; + if (module.status === MODULE_STATUS_EVALUATING) + return index; // Step 4 assert(module.status === MODULE_STATUS_INSTANTIATED, - "Bad module status in ModuleEvaluation"); + "Bad module status in InnerModuleEvaluation"); // Step 5 ModuleSetStatus(module, MODULE_STATUS_EVALUATING); @@ -605,8 +593,8 @@ function InnerModuleEvaluation(module, stack, index) index = InnerModuleEvaluation(requiredModule, stack, index); - assert(requiredModule.status == MODULE_STATUS_EVALUATING || - requiredModule.status == MODULE_STATUS_EVALUATED, + assert(requiredModule.status === MODULE_STATUS_EVALUATING || + requiredModule.status === MODULE_STATUS_EVALUATED, "Bad module status after InnerModuleEvaluation"); assert((requiredModule.status === MODULE_STATUS_EVALUATING) === |