summaryrefslogtreecommitdiffstats
path: root/js
diff options
context:
space:
mode:
Diffstat (limited to 'js')
-rw-r--r--js/public/Class.h2
-rw-r--r--js/public/HeapAPI.h37
-rw-r--r--js/src/builtin/Module.js300
-rw-r--r--js/src/builtin/ModuleObject.cpp121
-rw-r--r--js/src/builtin/ModuleObject.h33
-rw-r--r--js/src/builtin/SelfHostingDefines.h24
-rw-r--r--js/src/builtin/TestingFunctions.cpp8
-rw-r--r--js/src/gc/Marking.cpp5
-rw-r--r--js/src/jit-test/tests/modules/bug-1284486.js12
-rw-r--r--js/src/jit-test/tests/modules/bug-1420420-2.js19
-rw-r--r--js/src/jit-test/tests/modules/bug-1420420-3.js9
-rw-r--r--js/src/jit-test/tests/modules/bug-1420420-4.js16
-rw-r--r--js/src/jit-test/tests/modules/bug-1420420.js19
-rw-r--r--js/src/js.msg1
-rw-r--r--js/src/jsapi.cpp32
-rw-r--r--js/src/jsapi.h21
-rw-r--r--js/src/moz.build2
-rw-r--r--js/src/shell/js.cpp33
-rw-r--r--js/src/vm/EnvironmentObject.cpp2
-rw-r--r--js/src/vm/GlobalObject.h14
-rw-r--r--js/src/vm/Runtime.cpp6
-rw-r--r--js/src/vm/Runtime.h3
-rw-r--r--js/src/vm/SelfHosting.cpp28
23 files changed, 423 insertions, 324 deletions
diff --git a/js/public/Class.h b/js/public/Class.h
index 3d73bce44..f4fa9ccaf 100644
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -779,7 +779,7 @@ struct JSClass {
// application.
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
#define JSCLASS_GLOBAL_SLOT_COUNT \
- (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 46)
+ (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 45)
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
#define JSCLASS_GLOBAL_FLAGS \
diff --git a/js/public/HeapAPI.h b/js/public/HeapAPI.h
index fef6c0c78..d033d3706 100644
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -51,6 +51,7 @@ const size_t ChunkMarkBitmapBits = 129024;
const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*);
const size_t ChunkTrailerSize = 2 * sizeof(uintptr_t) + sizeof(uint64_t);
const size_t ChunkLocationOffset = ChunkSize - ChunkTrailerSize;
+const size_t ChunkStoreBufferOffset = ChunkSize - ChunkTrailerSize + sizeof(uint64_t);
const size_t ArenaZoneOffset = sizeof(size_t);
const size_t ArenaHeaderSize = sizeof(size_t) + 2 * sizeof(uintptr_t) +
sizeof(size_t) + sizeof(uintptr_t);
@@ -326,6 +327,20 @@ CellIsMarkedGray(const Cell* cell)
extern JS_PUBLIC_API(bool)
CellIsMarkedGrayIfKnown(const Cell* cell);
+MOZ_ALWAYS_INLINE ChunkLocation GetCellLocation(const void* cell) {
+ uintptr_t addr = uintptr_t(cell);
+ addr &= ~js::gc::ChunkMask;
+ addr |= js::gc::ChunkLocationOffset;
+ return *reinterpret_cast<ChunkLocation*>(addr);
+}
+
+MOZ_ALWAYS_INLINE bool NurseryCellHasStoreBuffer(const void* cell) {
+ uintptr_t addr = uintptr_t(cell);
+ addr &= ~js::gc::ChunkMask;
+ addr |= js::gc::ChunkStoreBufferOffset;
+ return *reinterpret_cast<void**>(addr) != nullptr;
+}
+
} /* namespace detail */
MOZ_ALWAYS_INLINE bool
@@ -341,6 +356,28 @@ IsInsideNursery(const js::gc::Cell* cell)
return location == ChunkLocation::Nursery;
}
+MOZ_ALWAYS_INLINE bool IsCellPointerValid(const void* cell) {
+ auto addr = uintptr_t(cell);
+ if (addr < ChunkSize || addr % CellSize != 0) {
+ return false;
+ }
+ auto location = detail::GetCellLocation(cell);
+ if (location == ChunkLocation::TenuredHeap) {
+ return !!detail::GetGCThingZone(addr);
+ }
+ if (location == ChunkLocation::Nursery) {
+ return detail::NurseryCellHasStoreBuffer(cell);
+ }
+ return false;
+}
+
+MOZ_ALWAYS_INLINE bool IsCellPointerValidOrNull(const void* cell) {
+ if (!cell) {
+ return true;
+ }
+ return IsCellPointerValid(cell);
+}
+
} /* namespace gc */
} /* namespace js */
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) ===
diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp
index 30e7120c0..728929e8c 100644
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -18,10 +18,10 @@
using namespace js;
using namespace js::frontend;
-static_assert(MODULE_STATUS_ERRORED < MODULE_STATUS_UNINSTANTIATED &&
- MODULE_STATUS_UNINSTANTIATED < MODULE_STATUS_INSTANTIATING &&
+static_assert(MODULE_STATUS_UNINSTANTIATED < MODULE_STATUS_INSTANTIATING &&
MODULE_STATUS_INSTANTIATING < MODULE_STATUS_INSTANTIATED &&
- MODULE_STATUS_INSTANTIATED < MODULE_STATUS_EVALUATED,
+ MODULE_STATUS_INSTANTIATED < MODULE_STATUS_EVALUATED &&
+ MODULE_STATUS_EVALUATED < MODULE_STATUS_EVALUATED_ERROR,
"Module statuses are ordered incorrectly");
template<typename T, Value ValueGetter(const T* obj)>
@@ -248,21 +248,13 @@ IndirectBindingMap::Binding::Binding(ModuleEnvironmentObject* environment, Shape
: environment(environment), shape(shape)
{}
-IndirectBindingMap::IndirectBindingMap(Zone* zone)
- : map_(ZoneAllocPolicy(zone))
-{
-}
-
-bool
-IndirectBindingMap::init()
-{
- return map_.init();
-}
-
void
IndirectBindingMap::trace(JSTracer* trc)
{
- for (Map::Enum e(map_); !e.empty(); e.popFront()) {
+ if (!map_)
+ return;
+
+ for (Map::Enum e(*map_); !e.empty(); e.popFront()) {
Binding& b = e.front().value();
TraceEdge(trc, &b.environment, "module bindings environment");
TraceEdge(trc, &b.shape, "module bindings shape");
@@ -273,12 +265,25 @@ IndirectBindingMap::trace(JSTracer* trc)
}
bool
-IndirectBindingMap::putNew(JSContext* cx, HandleId name,
- HandleModuleEnvironmentObject environment, HandleId localName)
-{
+IndirectBindingMap::put(JSContext* cx, HandleId name,
+ HandleModuleEnvironmentObject environment, HandleId localName)
+{
+ // This object might have been allocated on the background parsing thread in
+ // different zone to the final module. Lazily allocate the map so we don't
+ // have to switch its zone when merging compartments.
+ if (!map_) {
+ MOZ_ASSERT(!cx->zone()->usedByExclusiveThread);
+ map_.emplace(cx->zone());
+ if (!map_->init()) {
+ map_.reset();
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ }
+
RootedShape shape(cx, environment->lookup(cx, localName));
MOZ_ASSERT(shape);
- if (!map_.putNew(name, Binding(environment, shape))) {
+ if (!map_->put(name, Binding(environment, shape))) {
ReportOutOfMemory(cx);
return false;
}
@@ -289,7 +294,10 @@ IndirectBindingMap::putNew(JSContext* cx, HandleId name,
bool
IndirectBindingMap::lookup(jsid name, ModuleEnvironmentObject** envOut, Shape** shapeOut) const
{
- auto ptr = map_.lookup(name);
+ if (!map_)
+ return false;
+
+ auto ptr = map_->lookup(name);
if (!ptr)
return false;
@@ -359,7 +367,7 @@ ModuleNamespaceObject::addBinding(JSContext* cx, HandleAtom exportedName,
RootedModuleEnvironmentObject environment(cx, &targetModule->initialEnvironment());
RootedId exportedNameId(cx, AtomToId(exportedName));
RootedId localNameId(cx, AtomToId(localName));
- return bindings->putNew(cx, exportedNameId, environment, localNameId);
+ return bindings->put(cx, exportedNameId, environment, localNameId);
}
const char ModuleNamespaceObject::ProxyHandler::family = 0;
@@ -625,10 +633,9 @@ ModuleObject::create(ExclusiveContext* cx)
RootedModuleObject self(cx, &obj->as<ModuleObject>());
Zone* zone = cx->zone();
- IndirectBindingMap* bindings = zone->new_<IndirectBindingMap>(zone);
- if (!bindings || !bindings->init()) {
+ IndirectBindingMap* bindings = zone->new_<IndirectBindingMap>();
+ if (!bindings) {
ReportOutOfMemory(cx);
- js_delete<IndirectBindingMap>(bindings);
return nullptr;
}
@@ -657,14 +664,24 @@ ModuleObject::finalize(js::FreeOp* fop, JSObject* obj)
fop->delete_(funDecls);
}
+ModuleEnvironmentObject&
+ModuleObject::initialEnvironment() const
+{
+ Value value = getReservedSlot(EnvironmentSlot);
+ return value.toObject().as<ModuleEnvironmentObject>();
+}
+
ModuleEnvironmentObject*
ModuleObject::environment() const
{
- Value value = getReservedSlot(EnvironmentSlot);
- if (value.isUndefined())
+ MOZ_ASSERT(!hadEvaluationError());
+
+ // According to the spec the environment record is created during
+ // instantiation, but we create it earlier than that.
+ if (status() < MODULE_STATUS_INSTANTIATED)
return nullptr;
- return &value.toObject().as<ModuleEnvironmentObject>();
+ return &initialEnvironment();
}
bool
@@ -723,13 +740,13 @@ void
ModuleObject::init(HandleScript script)
{
initReservedSlot(ScriptSlot, PrivateValue(script));
- initReservedSlot(StatusSlot, Int32Value(MODULE_STATUS_ERRORED));
+ initReservedSlot(StatusSlot, Int32Value(MODULE_STATUS_UNINSTANTIATED));
}
void
ModuleObject::setInitialEnvironment(HandleModuleEnvironmentObject initialEnvironment)
{
- initReservedSlot(InitialEnvironmentSlot, ObjectValue(*initialEnvironment));
+ initReservedSlot(EnvironmentSlot, ObjectValue(*initialEnvironment));
}
void
@@ -827,7 +844,8 @@ ModuleObject::script() const
static inline void
AssertValidModuleStatus(ModuleStatus status)
{
- MOZ_ASSERT(status >= MODULE_STATUS_ERRORED && status <= MODULE_STATUS_EVALUATED);
+ MOZ_ASSERT(status >= MODULE_STATUS_UNINSTANTIATED &&
+ status <= MODULE_STATUS_EVALUATED_ERROR);
}
ModuleStatus
@@ -838,11 +856,17 @@ ModuleObject::status() const
return status;
}
+bool
+ModuleObject::hadEvaluationError() const
+{
+ return status() == MODULE_STATUS_EVALUATED_ERROR;
+}
+
Value
-ModuleObject::error() const
+ModuleObject::evaluationError() const
{
- MOZ_ASSERT(status() == MODULE_STATUS_ERRORED);
- return getReservedSlot(ErrorSlot);
+ MOZ_ASSERT(hadEvaluationError());
+ return getReservedSlot(EvaluationErrorSlot);
}
Value
@@ -857,12 +881,6 @@ ModuleObject::setHostDefinedField(const JS::Value& value)
setReservedSlot(HostDefinedSlot, value);
}
-ModuleEnvironmentObject&
-ModuleObject::initialEnvironment() const
-{
- return getReservedSlot(InitialEnvironmentSlot).toObject().as<ModuleEnvironmentObject>();
-}
-
Scope*
ModuleObject::enclosingScope() const
{
@@ -888,16 +906,6 @@ ModuleObject::trace(JSTracer* trc, JSObject* obj)
funDecls->trace(trc);
}
-void
-ModuleObject::createEnvironment()
-{
- // The environment has already been created, we just neet to set it in the
- // right slot.
- MOZ_ASSERT(!getReservedSlot(InitialEnvironmentSlot).isUndefined());
- MOZ_ASSERT(getReservedSlot(EnvironmentSlot).isUndefined());
- setReservedSlot(EnvironmentSlot, getReservedSlot(InitialEnvironmentSlot));
-}
-
bool
ModuleObject::noteFunctionDeclaration(ExclusiveContext* cx, HandleAtom name, HandleFunction fun)
{
@@ -913,7 +921,10 @@ ModuleObject::noteFunctionDeclaration(ExclusiveContext* cx, HandleAtom name, Han
/* static */ bool
ModuleObject::instantiateFunctionDeclarations(JSContext* cx, HandleModuleObject self)
{
+#ifdef DEBUG
+ MOZ_ASSERT(self->status() == MODULE_STATUS_INSTANTIATING);
MOZ_ASSERT(IsFrozen(cx, self));
+#endif
FunctionDeclarationVector* funDecls = self->functionDeclarations();
if (!funDecls) {
@@ -944,7 +955,10 @@ ModuleObject::instantiateFunctionDeclarations(JSContext* cx, HandleModuleObject
/* static */ bool
ModuleObject::execute(JSContext* cx, HandleModuleObject self, MutableHandleValue rval)
{
+#ifdef DEBUG
+ MOZ_ASSERT(self->status() == MODULE_STATUS_EVALUATING);
MOZ_ASSERT(IsFrozen(cx, self));
+#endif
RootedScript script(cx, self->script());
RootedModuleEnvironmentObject scope(cx, self->environment());
@@ -967,10 +981,9 @@ ModuleObject::createNamespace(JSContext* cx, HandleModuleObject self, HandleObje
return nullptr;
Zone* zone = cx->zone();
- IndirectBindingMap* bindings = zone->new_<IndirectBindingMap>(zone);
- if (!bindings || !bindings->init()) {
+ IndirectBindingMap* bindings = zone->new_<IndirectBindingMap>();
+ if (!bindings) {
ReportOutOfMemory(cx);
- js_delete<IndirectBindingMap>(bindings);
return nullptr;
}
@@ -1005,7 +1018,7 @@ ModuleObject::Evaluate(JSContext* cx, HandleModuleObject self)
DEFINE_GETTER_FUNCTIONS(ModuleObject, namespace_, NamespaceSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, status, StatusSlot)
-DEFINE_GETTER_FUNCTIONS(ModuleObject, error, ErrorSlot)
+DEFINE_GETTER_FUNCTIONS(ModuleObject, evaluationError, EvaluationErrorSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, requestedModules, RequestedModulesSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, importEntries, ImportEntriesSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, localExportEntries, LocalExportEntriesSlot)
@@ -1020,7 +1033,7 @@ GlobalObject::initModuleProto(JSContext* cx, Handle<GlobalObject*> global)
static const JSPropertySpec protoAccessors[] = {
JS_PSG("namespace", ModuleObject_namespace_Getter, 0),
JS_PSG("status", ModuleObject_statusGetter, 0),
- JS_PSG("error", ModuleObject_errorGetter, 0),
+ JS_PSG("evaluationError", ModuleObject_evaluationErrorGetter, 0),
JS_PSG("requestedModules", ModuleObject_requestedModulesGetter, 0),
JS_PSG("importEntries", ModuleObject_importEntriesGetter, 0),
JS_PSG("localExportEntries", ModuleObject_localExportEntriesGetter, 0),
diff --git a/js/src/builtin/ModuleObject.h b/js/src/builtin/ModuleObject.h
index be0315215..5e4e7f342 100644
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -7,6 +7,8 @@
#ifndef builtin_ModuleObject_h
#define builtin_ModuleObject_h
+#include "mozilla/Maybe.h"
+
#include "jsapi.h"
#include "jsatom.h"
@@ -102,27 +104,27 @@ typedef Handle<ExportEntryObject*> HandleExportEntryObject;
class IndirectBindingMap
{
public:
- explicit IndirectBindingMap(Zone* zone);
- bool init();
-
void trace(JSTracer* trc);
- bool putNew(JSContext* cx, HandleId name,
- HandleModuleEnvironmentObject environment, HandleId localName);
+ bool put(JSContext* cx, HandleId name,
+ HandleModuleEnvironmentObject environment, HandleId localName);
size_t count() const {
- return map_.count();
+ return map_ ? map_->count() : 0;
}
bool has(jsid name) const {
- return map_.has(name);
+ return map_ ? map_->has(name) : false;
}
bool lookup(jsid name, ModuleEnvironmentObject** envOut, Shape** shapeOut) const;
template <typename Func>
void forEachExportedName(Func func) const {
- for (auto r = map_.all(); !r.empty(); r.popFront())
+ if (!map_)
+ return;
+
+ for (auto r = map_->all(); !r.empty(); r.popFront())
func(r.front().key());
}
@@ -136,7 +138,7 @@ class IndirectBindingMap
typedef HashMap<jsid, Binding, DefaultHasher<jsid>, ZoneAllocPolicy> Map;
- Map map_;
+ mozilla::Maybe<Map> map_;
};
class ModuleNamespaceObject : public ProxyObject
@@ -214,11 +216,10 @@ class ModuleObject : public NativeObject
enum
{
ScriptSlot = 0,
- InitialEnvironmentSlot,
EnvironmentSlot,
NamespaceSlot,
StatusSlot,
- ErrorSlot,
+ EvaluationErrorSlot,
HostDefinedSlot,
RequestedModulesSlot,
ImportEntriesSlot,
@@ -238,8 +239,8 @@ class ModuleObject : public NativeObject
"EnvironmentSlot must match self-hosting define");
static_assert(StatusSlot == MODULE_OBJECT_STATUS_SLOT,
"StatusSlot must match self-hosting define");
- static_assert(ErrorSlot == MODULE_OBJECT_ERROR_SLOT,
- "ErrorSlot must match self-hosting define");
+ static_assert(EvaluationErrorSlot == MODULE_OBJECT_EVALUATION_ERROR_SLOT,
+ "EvaluationErrorSlot must match self-hosting define");
static_assert(DFSIndexSlot == MODULE_OBJECT_DFS_INDEX_SLOT,
"DFSIndexSlot must match self-hosting define");
static_assert(DFSAncestorIndexSlot == MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT,
@@ -269,7 +270,8 @@ class ModuleObject : public NativeObject
ModuleEnvironmentObject* environment() const;
ModuleNamespaceObject* namespace_();
ModuleStatus status() const;
- Value error() const;
+ bool hadEvaluationError() const;
+ Value evaluationError() const;
Value hostDefinedField() const;
ArrayObject& requestedModules() const;
ArrayObject& importEntries() const;
@@ -285,9 +287,6 @@ class ModuleObject : public NativeObject
void setHostDefinedField(const JS::Value& value);
- // For intrinsic_CreateModuleEnvironment.
- void createEnvironment();
-
// For BytecodeEmitter.
bool noteFunctionDeclaration(ExclusiveContext* cx, HandleAtom name, HandleFunction fun);
diff --git a/js/src/builtin/SelfHostingDefines.h b/js/src/builtin/SelfHostingDefines.h
index a06c2aa62..a3ba36804 100644
--- a/js/src/builtin/SelfHostingDefines.h
+++ b/js/src/builtin/SelfHostingDefines.h
@@ -97,17 +97,17 @@
#define REGEXP_STRING_ITERATOR_FLAGS_SLOT 2
#define REGEXP_STRING_ITERATOR_DONE_SLOT 3
-#define MODULE_OBJECT_ENVIRONMENT_SLOT 2
-#define MODULE_OBJECT_STATUS_SLOT 4
-#define MODULE_OBJECT_ERROR_SLOT 5
-#define MODULE_OBJECT_DFS_INDEX_SLOT 16
-#define MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT 17
-
-#define MODULE_STATUS_ERRORED 0
-#define MODULE_STATUS_UNINSTANTIATED 1
-#define MODULE_STATUS_INSTANTIATING 2
-#define MODULE_STATUS_INSTANTIATED 3
-#define MODULE_STATUS_EVALUATING 4
-#define MODULE_STATUS_EVALUATED 5
+#define MODULE_OBJECT_ENVIRONMENT_SLOT 1
+#define MODULE_OBJECT_STATUS_SLOT 3
+#define MODULE_OBJECT_EVALUATION_ERROR_SLOT 4
+#define MODULE_OBJECT_DFS_INDEX_SLOT 15
+#define MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT 16
+
+#define MODULE_STATUS_UNINSTANTIATED 0
+#define MODULE_STATUS_INSTANTIATING 1
+#define MODULE_STATUS_INSTANTIATED 2
+#define MODULE_STATUS_EVALUATING 3
+#define MODULE_STATUS_EVALUATED 4
+#define MODULE_STATUS_EVALUATED_ERROR 5
#endif
diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
index 025620766..f589b8076 100644
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -3566,8 +3566,6 @@ GetModuleEnvironment(JSContext* cx, HandleValue moduleValue)
// before they have been instantiated.
RootedModuleEnvironmentObject env(cx, &module->initialEnvironment());
MOZ_ASSERT(env);
- MOZ_ASSERT_IF(module->environment(), module->environment() == env);
-
return env;
}
@@ -3585,6 +3583,9 @@ GetModuleEnvironmentNames(JSContext* cx, unsigned argc, Value* vp)
return false;
}
+//- if (module->status() == MODULE_STATUS_ERRORED) {
+//+ if (module->hadEvaluationError()) {
+
RootedModuleEnvironmentObject env(cx, GetModuleEnvironment(cx, args[0]));
Rooted<IdVector> ids(cx, IdVector(cx));
if (!JS_Enumerate(cx, env, &ids))
@@ -3622,6 +3623,9 @@ GetModuleEnvironmentValue(JSContext* cx, unsigned argc, Value* vp)
return false;
}
+//- if (module->status() == MODULE_STATUS_ERRORED) {
+//+ if (module->hadEvaluationError()) {
+
RootedModuleEnvironmentObject env(cx, GetModuleEnvironment(cx, args[0]));
RootedString name(cx, args[1].toString());
RootedId id(cx);
diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp
index 43e325394..b4db0297a 100644
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -2267,6 +2267,8 @@ void
js::gc::StoreBuffer::SlotsEdge::trace(TenuringTracer& mover) const
{
NativeObject* obj = object();
+ if(!IsCellPointerValid(obj))
+ return;
// Beware JSObject::swap exchanging a native object for a non-native one.
if (!obj->isNative())
@@ -2336,6 +2338,8 @@ js::gc::StoreBuffer::traceWholeCells(TenuringTracer& mover)
{
for (ArenaCellSet* cells = bufferWholeCell; cells; cells = cells->next) {
Arena* arena = cells->arena;
+ if(!IsCellPointerValid(arena))
+ continue;
MOZ_ASSERT(arena->bufferedCells == cells);
arena->bufferedCells = &ArenaCellSet::Empty;
@@ -2364,6 +2368,7 @@ js::gc::StoreBuffer::CellPtrEdge::trace(TenuringTracer& mover) const
{
if (!*edge)
return;
+ // XXX: We should check if the cell pointer is valid here too
MOZ_ASSERT((*edge)->getTraceKind() == JS::TraceKind::Object);
mover.traverse(reinterpret_cast<JSObject**>(edge));
diff --git a/js/src/jit-test/tests/modules/bug-1284486.js b/js/src/jit-test/tests/modules/bug-1284486.js
index 08286393a..6fbd67d9d 100644
--- a/js/src/jit-test/tests/modules/bug-1284486.js
+++ b/js/src/jit-test/tests/modules/bug-1284486.js
@@ -1,9 +1,8 @@
-// This tests that attempting to perform ModuleDeclarationInstantation a second
-// time after a failure re-throws the same error.
+// This tests that module instantiation can succeed when executed a second
+// time after a failure.
//
// The first attempt fails becuase module 'a' is not available. The second
-// attempt fails because of the previous failure (it would otherwise succeed as
-// 'a' is now available).
+// attempt succeeds as 'a' is now available.
load(libdir + "dummyModuleResolveHook.js");
@@ -25,12 +24,9 @@ let a = moduleRepo['a'] = parseModule("export var a = 1; export var b = 2;");
let d = moduleRepo['d'] = parseModule("import { a } from 'c'; a;");
threw = false;
-let e2;
try {
d.declarationInstantiation();
} catch (exc) {
threw = true;
- e2 = exc;
}
-assertEq(threw, true);
-assertEq(e1, e2);
+assertEq(threw, false);
diff --git a/js/src/jit-test/tests/modules/bug-1420420-2.js b/js/src/jit-test/tests/modules/bug-1420420-2.js
new file mode 100644
index 000000000..0511e8126
--- /dev/null
+++ b/js/src/jit-test/tests/modules/bug-1420420-2.js
@@ -0,0 +1,19 @@
+// Test re-instantiation module after failure.
+
+load(libdir + "asserts.js");
+load(libdir + "dummyModuleResolveHook.js");
+
+moduleRepo["good"] = parseModule(`export let x`);
+
+moduleRepo["y1"] = parseModule(`export let y`);
+moduleRepo["y2"] = parseModule(`export let y`);
+moduleRepo["bad"] = parseModule(`export* from "y1"; export* from "y2";`);
+
+moduleRepo["a"] = parseModule(`import* as ns from "good"; import {y} from "bad";`);
+
+let b = moduleRepo["b"] = parseModule(`import "a";`);
+let c = moduleRepo["c"] = parseModule(`import "a";`);
+
+assertThrowsInstanceOf(() => b.declarationInstantiation(), SyntaxError);
+assertThrowsInstanceOf(() => c.declarationInstantiation(), SyntaxError);
+
diff --git a/js/src/jit-test/tests/modules/bug-1420420-3.js b/js/src/jit-test/tests/modules/bug-1420420-3.js
new file mode 100644
index 000000000..236c023b9
--- /dev/null
+++ b/js/src/jit-test/tests/modules/bug-1420420-3.js
@@ -0,0 +1,9 @@
+if (!('stackTest' in this))
+ quit();
+
+let a = parseModule(`throw new Error`);
+a.declarationInstantiation();
+stackTest(function() {
+ a.evaluation();
+});
+
diff --git a/js/src/jit-test/tests/modules/bug-1420420-4.js b/js/src/jit-test/tests/modules/bug-1420420-4.js
new file mode 100644
index 000000000..721c770bc
--- /dev/null
+++ b/js/src/jit-test/tests/modules/bug-1420420-4.js
@@ -0,0 +1,16 @@
+load(libdir + "asserts.js");
+load(libdir + "dummyModuleResolveHook.js");
+
+moduleRepo["a"] = parseModule(`throw undefined`);
+
+let b = moduleRepo["b"] = parseModule(`import "a";`);
+let c = moduleRepo["c"] = parseModule(`import "a";`);
+
+b.declarationInstantiation();
+c.declarationInstantiation();
+
+let count = 0;
+try { b.evaluation() } catch (e) { count++; }
+try { c.evaluation() } catch (e) { count++; }
+assertEq(count, 2);
+
diff --git a/js/src/jit-test/tests/modules/bug-1420420.js b/js/src/jit-test/tests/modules/bug-1420420.js
new file mode 100644
index 000000000..cd1101c76
--- /dev/null
+++ b/js/src/jit-test/tests/modules/bug-1420420.js
@@ -0,0 +1,19 @@
+// Test re-instantiation module after failure.
+
+load(libdir + "asserts.js");
+load(libdir + "dummyModuleResolveHook.js");
+
+moduleRepo["good"] = parseModule(`export let x`);
+
+moduleRepo["y1"] = parseModule(`export let y`);
+moduleRepo["y2"] = parseModule(`export let y`);
+moduleRepo["bad"] = parseModule(`export* from "y1"; export* from "y2";`);
+
+moduleRepo["a"] = parseModule(`import {x} from "good"; import {y} from "bad";`);
+
+let b = moduleRepo["b"] = parseModule(`import "a";`);
+let c = moduleRepo["c"] = parseModule(`import "a";`);
+
+assertThrowsInstanceOf(() => b.declarationInstantiation(), SyntaxError);
+assertThrowsInstanceOf(() => c.declarationInstantiation(), SyntaxError);
+
diff --git a/js/src/js.msg b/js/src/js.msg
index 9c508ebbd..587481864 100644
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -579,7 +579,6 @@ MSG_DEF(JSMSG_MISSING_IMPORT, 0, JSEXN_SYNTAXERR, "import not found")
MSG_DEF(JSMSG_AMBIGUOUS_IMPORT, 0, JSEXN_SYNTAXERR, "ambiguous import")
MSG_DEF(JSMSG_MISSING_NAMESPACE_EXPORT, 0, JSEXN_SYNTAXERR, "export not found for namespace")
MSG_DEF(JSMSG_MISSING_EXPORT, 1, JSEXN_SYNTAXERR, "local binding for export '{0}' not found")
-MSG_DEF(JSMSG_MODULE_INSTANTIATE_FAILED, 0, JSEXN_INTERNALERR, "attempt to re-instantiate module after failure")
MSG_DEF(JSMSG_BAD_MODULE_STATUS, 0, JSEXN_INTERNALERR, "module record has unexpected status")
// Promise
diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
index cf5880e03..69a3ba2ac 100644
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -483,6 +483,11 @@ JS_DestroyContext(JSContext* cx)
DestroyContext(cx);
}
+JS_PUBLIC_API(JSRuntime*)
+JS_GetRuntime(JSContext* cx) {
+ return cx->runtime();
+}
+
static JS_CurrentEmbedderTimeFunction currentEmbedderTimeFunction;
JS_PUBLIC_API(void)
@@ -4662,21 +4667,16 @@ JS::Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
return ::Evaluate(cx, optionsArg, filename, rval);
}
-JS_PUBLIC_API(JSFunction*)
-JS::GetModuleResolveHook(JSContext* cx)
+JS_PUBLIC_API(JS::ModuleResolveHook)
+JS::GetModuleResolveHook(JSRuntime* rt)
{
- AssertHeapIsIdle(cx);
- CHECK_REQUEST(cx);
- return cx->global()->moduleResolveHook();
+ return rt->moduleResolveHook;
}
JS_PUBLIC_API(void)
-JS::SetModuleResolveHook(JSContext* cx, HandleFunction func)
+JS::SetModuleResolveHook(JSRuntime* rt, JS::ModuleResolveHook func)
{
- AssertHeapIsIdle(cx);
- CHECK_REQUEST(cx);
- assertSameCompartment(cx, func);
- cx->global()->setModuleResolveHook(func);
+ rt->moduleResolveHook = func;
}
JS_PUBLIC_API(bool)
@@ -4739,18 +4739,6 @@ JS::GetModuleScript(JSContext* cx, JS::HandleObject moduleArg)
return moduleArg->as<ModuleObject>().script();
}
-JS_PUBLIC_API(bool)
-JS::IsModuleErrored(JSObject* moduleArg)
-{
- return moduleArg->as<ModuleObject>().status() == MODULE_STATUS_ERRORED;
-}
-
-JS_PUBLIC_API(JS::Value)
-JS::GetModuleError(JSObject* moduleArg)
-{
- return moduleArg->as<ModuleObject>().error();
-}
-
JS_PUBLIC_API(JSObject*)
JS_New(JSContext* cx, HandleObject ctor, const JS::HandleValueArray& inputArgs)
{
diff --git a/js/src/jsapi.h b/js/src/jsapi.h
index 9c3bf8151..5cdfd958e 100644
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -995,6 +995,9 @@ JS_NewContext(uint32_t maxbytes,
extern JS_PUBLIC_API(void)
JS_DestroyContext(JSContext* cx);
+extern JS_PUBLIC_API(JSRuntime*)
+JS_GetRuntime(JSContext* cx);
+
typedef double (*JS_CurrentEmbedderTimeFunction)();
/**
@@ -4322,17 +4325,19 @@ extern JS_PUBLIC_API(bool)
Evaluate(JSContext* cx, const ReadOnlyCompileOptions& options,
const char* filename, JS::MutableHandleValue rval);
+using ModuleResolveHook = JSObject* (*)(JSContext*, HandleObject, HandleString);
+
/**
- * Get the HostResolveImportedModule hook for a global.
+ * Get the HostResolveImportedModule hook for the runtime.
*/
-extern JS_PUBLIC_API(JSFunction*)
-GetModuleResolveHook(JSContext* cx);
+extern JS_PUBLIC_API(ModuleResolveHook)
+GetModuleResolveHook(JSRuntime* rt);
/**
- * Set the HostResolveImportedModule hook for a global to the given function.
+ * Set the HostResolveImportedModule hook for the runtime to the given function.
*/
extern JS_PUBLIC_API(void)
-SetModuleResolveHook(JSContext* cx, JS::HandleFunction func);
+SetModuleResolveHook(JSRuntime* rt, ModuleResolveHook func);
/**
* Parse the given source buffer as a module in the scope of the current global
@@ -4395,12 +4400,6 @@ GetRequestedModules(JSContext* cx, JS::HandleObject moduleRecord);
extern JS_PUBLIC_API(JSScript*)
GetModuleScript(JSContext* cx, JS::HandleObject moduleRecord);
-extern JS_PUBLIC_API(bool)
-IsModuleErrored(JSObject* moduleRecord);
-
-extern JS_PUBLIC_API(JS::Value)
-GetModuleError(JSObject* moduleRecord);
-
} /* namespace JS */
extern JS_PUBLIC_API(bool)
diff --git a/js/src/moz.build b/js/src/moz.build
index c0cef9929..3e6402f71 100644
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -687,8 +687,8 @@ if CONFIG['_MSC_VER']:
elif CONFIG['CPU_ARCH'] == 'x86_64' and CONFIG['JS_HAS_CTYPES']:
SOURCES['ctypes/CTypes.cpp'].no_pgo = True # Bug 810661
# Prevent floating point errors caused by VC++ optimizations
- # XXX We should add this to CXXFLAGS, too?
CFLAGS += ['-fp:precise']
+ CXXFLAGS += ['-fp:precise']
# C4805 warns mixing bool with other integral types in computation.
# But given the conversion from bool is specified, and this is a
# pattern widely used in code in js/src, suppress this warning here.
diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
index 8cd821b31..bbf35459f 100644
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -274,6 +274,7 @@ struct ShellContext
JS::PersistentRooted<JobQueue> jobQueue;
ExclusiveData<ShellAsyncTasks> asyncTasks;
bool drainingJobQueue;
+ JS::PersistentRootedFunction moduleResolveHook;
/*
* Watchdog thread state.
@@ -439,7 +440,8 @@ ShellContext::ShellContext(JSContext* cx)
exitCode(0),
quitting(false),
readLineBufPos(0),
- spsProfilingStackSize(0)
+ spsProfilingStackSize(0),
+ moduleResolveHook(cx)
{}
static ShellContext*
@@ -4030,13 +4032,34 @@ SetModuleResolveHook(JSContext* cx, unsigned argc, Value* vp)
return false;
}
- RootedFunction hook(cx, &args[0].toObject().as<JSFunction>());
- Rooted<GlobalObject*> global(cx, cx->global());
- global->setModuleResolveHook(hook);
+ ShellContext* sc = GetShellContext(cx);
+ sc->moduleResolveHook = &args[0].toObject().as<JSFunction>();
+
args.rval().setUndefined();
return true;
}
+static JSObject*
+CallModuleResolveHook(JSContext* cx, HandleObject module, HandleString specifier)
+{
+ ShellContext* sc = GetShellContext(cx);
+
+ JS::AutoValueArray<2> args(cx);
+ args[0].setObject(*module);
+ args[1].setString(specifier);
+
+ RootedValue result(cx);
+ if (!JS_CallFunction(cx, nullptr, sc->moduleResolveHook, args, &result))
+ return nullptr;
+
+ if (!result.isObject() || !result.toObject().is<ModuleObject>()) {
+ JS_ReportErrorASCII(cx, "Module resolve hook did not return Module object");
+ return nullptr;
+ }
+
+ return &result.toObject();
+}
+
static bool
GetModuleLoadPath(JSContext* cx, unsigned argc, Value* vp)
{
@@ -7962,6 +7985,8 @@ main(int argc, char** argv, char** envp)
js::SetPreserveWrapperCallback(cx, DummyPreserveWrapperCallback);
+ JS::SetModuleResolveHook(cx->runtime(), CallModuleResolveHook);
+
result = Shell(cx, &op, envp);
#ifdef DEBUG
diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp
index 7834940f1..871ae3f19 100644
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -491,7 +491,7 @@ ModuleEnvironmentObject::createImportBinding(JSContext* cx, HandleAtom importNam
RootedId importNameId(cx, AtomToId(importName));
RootedId localNameId(cx, AtomToId(localName));
RootedModuleEnvironmentObject env(cx, &module->initialEnvironment());
- if (!importBindings().putNew(cx, importNameId, env, localNameId)) {
+ if (!importBindings().put(cx, importNameId, env, localNameId)) {
ReportOutOfMemory(cx);
return false;
}
diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h
index 720f63e69..a9f07d495 100644
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -119,7 +119,6 @@ class GlobalObject : public NativeObject
DEBUGGERS,
INTRINSICS,
FOR_OF_PIC_CHAIN,
- MODULE_RESOLVE_HOOK,
WINDOW_PROXY,
GLOBAL_THIS_RESOLVED,
@@ -879,19 +878,6 @@ class GlobalObject : public NativeObject
setReservedSlot(WINDOW_PROXY, ObjectValue(*windowProxy));
}
- void setModuleResolveHook(HandleFunction hook) {
- MOZ_ASSERT(hook);
- setSlot(MODULE_RESOLVE_HOOK, ObjectValue(*hook));
- }
-
- JSFunction* moduleResolveHook() {
- Value value = getSlotRef(MODULE_RESOLVE_HOOK);
- if (value.isUndefined())
- return nullptr;
-
- return &value.toObject().as<JSFunction>();
- }
-
// Returns either this global's star-generator function prototype, or null
// if that object was never created. Dodgy; for use only in also-dodgy
// GlobalHelperThreadState::mergeParseTaskCompartment().
diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp
index 284a4f3d7..5e0246b85 100644
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -241,8 +241,10 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
lastAnimationTime(0),
performanceMonitoring(thisFromCtor()),
ionLazyLinkListSize_(0),
- stackFormat_(parentRuntime ? js::StackFormat::Default
- : js::StackFormat::SpiderMonkey)
+ stackFormat_(parentRuntime ?
+ js::StackFormat::Default :
+ js::StackFormat::SpiderMonkey),
+ moduleResolveHook()
{
setGCStoreBufferPtr(&gc.storeBuffer);
diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h
index e60371e38..247a2dc9d 100644
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1294,6 +1294,9 @@ struct JSRuntime : public JS::shadow::Runtime,
// For inherited heap state accessors.
friend class js::gc::AutoTraceSession;
friend class JS::AutoEnterCycleCollection;
+
+ // The implementation-defined abstract operation HostResolveImportedModule.
+ JS::ModuleResolveHook moduleResolveHook;
};
namespace js {
diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
index 0dfeffc36..f324a0a67 100644
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2026,36 +2026,26 @@ intrinsic_HostResolveImportedModule(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
- MOZ_ASSERT(args[0].toObject().is<ModuleObject>());
- MOZ_ASSERT(args[1].isString());
+ RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
+ RootedString specifier(cx, args[1].toString());
- RootedFunction moduleResolveHook(cx, cx->global()->moduleResolveHook());
+ JS::ModuleResolveHook moduleResolveHook = cx->runtime()->moduleResolveHook;
if (!moduleResolveHook) {
JS_ReportErrorASCII(cx, "Module resolve hook not set");
return false;
}
- RootedValue result(cx);
- if (!JS_CallFunction(cx, nullptr, moduleResolveHook, args, &result))
+ RootedObject result(cx);
+ result = moduleResolveHook(cx, module, specifier);
+ if (!result)
return false;
- if (!result.isObject() || !result.toObject().is<ModuleObject>()) {
+ if (!result->is<ModuleObject>()) {
JS_ReportErrorASCII(cx, "Module resolve hook did not return Module object");
return false;
}
- args.rval().set(result);
- return true;
-}
-
-static bool
-intrinsic_CreateModuleEnvironment(JSContext* cx, unsigned argc, Value* vp)
-{
- CallArgs args = CallArgsFromVp(argc, vp);
- MOZ_ASSERT(args.length() == 1);
- RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
- module->createEnvironment();
- args.rval().setUndefined();
+ args.rval().setObject(*result);
return true;
}
@@ -2087,7 +2077,6 @@ intrinsic_CreateNamespaceBinding(JSContext* cx, unsigned argc, Value* vp)
// the slot directly.
RootedShape shape(cx, environment->lookup(cx, name));
MOZ_ASSERT(shape);
- MOZ_ASSERT(environment->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL));
environment->setSlot(shape->slot(), args[2]);
args.rval().setUndefined();
return true;
@@ -2659,7 +2648,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
CallNonGenericSelfhostedMethod<Is<ModuleObject>>, 2, 0),
JS_FN("HostResolveImportedModule", intrinsic_HostResolveImportedModule, 2, 0),
JS_FN("IsModuleEnvironment", intrinsic_IsInstanceOfBuiltin<ModuleEnvironmentObject>, 1, 0),
- JS_FN("CreateModuleEnvironment", intrinsic_CreateModuleEnvironment, 1, 0),
JS_FN("CreateImportBinding", intrinsic_CreateImportBinding, 4, 0),
JS_FN("CreateNamespaceBinding", intrinsic_CreateNamespaceBinding, 3, 0),
JS_FN("InstantiateModuleFunctionDeclarations",