summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/Module.js
diff options
context:
space:
mode:
authorMoonchild <moonchild@palemoon.org>2020-07-08 12:58:30 +0000
committerMoonchild <moonchild@palemoon.org>2020-07-08 12:58:30 +0000
commit43e0632cd474265ef0660bf881f4472996c8ad5a (patch)
tree70b3b307f37c54a66419995c74a3ac2d06ccb19a /js/src/builtin/Module.js
parent786e344698c5a577ea82e2fa0d29faf2a2d851ea (diff)
downloadUXP-43e0632cd474265ef0660bf881f4472996c8ad5a.tar
UXP-43e0632cd474265ef0660bf881f4472996c8ad5a.tar.gz
UXP-43e0632cd474265ef0660bf881f4472996c8ad5a.tar.lz
UXP-43e0632cd474265ef0660bf881f4472996c8ad5a.tar.xz
UXP-43e0632cd474265ef0660bf881f4472996c8ad5a.zip
Issue #618 - Align error handling for module scripts with the spec (again)
This updates module implementation to match spec regarding handling of instantiation errors, after it was changed yet again, this time to not remember instantiation errors, but instead immediately rethrow applicable ones. Ref: BZ 1420420
Diffstat (limited to 'js/src/builtin/Module.js')
-rw-r--r--js/src/builtin/Module.js292
1 files changed, 140 insertions, 152 deletions
diff --git a/js/src/builtin/Module.js b/js/src/builtin/Module.js
index 64d62d216..86b44880d 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) ===