summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/Module.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/builtin/Module.js')
-rw-r--r--js/src/builtin/Module.js300
1 files changed, 146 insertions, 154 deletions
diff --git a/js/src/builtin/Module.js b/js/src/builtin/Module.js
index 64d62d216..355303d44 100644
--- a/js/src/builtin/Module.js
+++ b/js/src/builtin/Module.js
@@ -67,10 +67,16 @@ function ModuleGetExportedNames(exportStarSet = [])
function ModuleSetStatus(module, newStatus)
{
- assert(newStatus >= MODULE_STATUS_ERRORED && newStatus <= MODULE_STATUS_EVALUATED,
+ assert(newStatus >= MODULE_STATUS_UNINSTANTIATED &&
+ newStatus <= MODULE_STATUS_EVALUATED_ERROR,
"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 +86,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 +107,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 +155,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 +188,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 +202,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 +224,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,31 +235,16 @@ 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);
+ assert(module.status >= MODULE_STATUS_INSTANTIATING,
+ "Attempt to access module environement before instantation");
let env = UnsafeGetReservedSlot(module, MODULE_OBJECT_ENVIRONMENT_SLOT);
- assert(env === undefined || IsModuleEnvironment(env),
+ assert(IsModuleEnvironment(env),
"Module environment slot contains unexpected value");
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 +264,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 +295,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 +330,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 +339,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 +388,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,16 +418,14 @@ 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);
}
}
// Steps 5-6
- CreateModuleEnvironment(module);
+ // Note that we have already created the environment by this point.
let env = GetModuleEnvironment(module);
// Step 8
@@ -458,12 +440,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 +452,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 +514,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 +534,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 +563,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 +597,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) ===