Bug 1374239 - Store and re-throw module instantiation and evaluation errors r=shu
authorJon Coppeard <jcoppeard@mozilla.com>
Wed, 09 Aug 2017 18:05:15 +0100
changeset 373868 79aa5930dbf10c62df6ec4d774e7e63ad32b36c7
parent 373613 411fe4772f31d9ec41fa95fc2e5e15c8df1c7133
child 373869 6395741da4657cbe8c65bb77a29a25caa63d3711
push id32311
push userkwierso@gmail.com
push dateFri, 11 Aug 2017 01:14:57 +0000
treeherdermozilla-central@253a8560dc34 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs1374239
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1374239 - Store and re-throw module instantiation and evaluation errors r=shu
js/src/builtin/Module.js
js/src/builtin/ModuleObject.cpp
js/src/builtin/ModuleObject.h
js/src/builtin/SelfHostingDefines.h
js/src/builtin/Utilities.js
js/src/jit-test/tests/modules/ambiguous-star-export.js
js/src/jit-test/tests/modules/bad-namespace-created.js
js/src/jit-test/tests/modules/bug-1284486-2.js
js/src/jit-test/tests/modules/bug-1284486.js
js/src/jit-test/tests/modules/bug-1287410.js
js/src/jit-test/tests/modules/global-scope.js
js/src/jit-test/tests/modules/module-evaluation.js
js/src/js.msg
js/src/jsapi.cpp
js/src/shell/ModuleLoader.js
js/src/vm/CommonPropertyNames.h
js/src/vm/SelfHosting.cpp
--- a/js/src/builtin/Module.js
+++ b/js/src/builtin/Module.js
@@ -1,17 +1,18 @@
+/* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-function CallModuleResolveHook(module, specifier, expectedMinimumState)
+function CallModuleResolveHook(module, specifier, expectedMinimumStatus)
 {
     let requestedModule = HostResolveImportedModule(module, specifier);
-    if (requestedModule.state < expectedMinimumState)
-        ThrowInternalError(JSMSG_BAD_MODULE_STATE);
+    if (requestedModule.status < expectedMinimumStatus)
+        ThrowInternalError(JSMSG_BAD_MODULE_STATUS);
 
     return requestedModule;
 }
 
 // 15.2.1.16.2 GetExportedNames(exportStarSet)
 function ModuleGetExportedNames(exportStarSet = [])
 {
     if (!IsObject(this) || !IsModule(this)) {
@@ -47,123 +48,168 @@ function ModuleGetExportedNames(exportSt
         _DefineDataProperty(exportedNames, namesCount++, e.exportName);
     }
 
     // Step 7
     let starExportEntries = module.starExportEntries;
     for (let i = 0; i < starExportEntries.length; i++) {
         let e = starExportEntries[i];
         let requestedModule = CallModuleResolveHook(module, e.moduleRequest,
-                                                    MODULE_STATE_INSTANTIATED);
+                                                    MODULE_STATUS_INSTANTIATING);
         let starNames = callFunction(requestedModule.getExportedNames, requestedModule,
                                      exportStarSet);
         for (let j = 0; j < starNames.length; j++) {
             let n = starNames[j];
             if (n !== "default" && !callFunction(ArrayIncludes, exportedNames, n))
                 _DefineDataProperty(exportedNames, namesCount++, n);
         }
     }
 
     return exportedNames;
 }
 
+function ModuleSetStatus(module, newStatus)
+{
+    assert(newStatus >= MODULE_STATUS_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");
+
+    UnsafeSetReservedSlot(module, MODULE_OBJECT_STATUS_SLOT, newStatus);
+}
+
 // 15.2.1.16.3 ResolveExport(exportName, resolveSet)
+//
+// Returns an object describing the location of the resolved export or
+// indicating a failure.
+//
+// On success this returns: { resolved: true, module, bindingName }
+//
+// There are three failure cases:
+//
+//  - The resolution failure can be blamed on a particular module.
+//    Returns: { resolved: false, module, ambiguous: false }
+//
+//  - No culprit can be determined and the resolution failure was due to star
+//    export ambiguity.
+//    Returns: { resolved: false, module: null, ambiguous: true }
+//
+//  - No culprit can be determined and the resolution failure was not due to
+//    star export ambiguity.
+//    Returns: { resolved: false, module: null, ambiguous: false }
+//
 function ModuleResolveExport(exportName, resolveSet = [])
 {
     if (!IsObject(this) || !IsModule(this)) {
         return callFunction(CallModuleMethodIfWrapped, this, exportName, resolveSet,
                             "ModuleResolveExport");
     }
 
     // Step 1
     let module = this;
 
     // Step 2
+    assert(module.status !== MODULE_STATUS_ERRORED, "Bad module state in ResolveExport");
+
+    // Step 3
     for (let i = 0; i < resolveSet.length; i++) {
         let r = resolveSet[i];
-        if (r.module === module && r.exportName === exportName)
-            return null;
+        if (r.module === module && r.exportName === exportName) {
+            // This is a circular import request.
+            return {resolved: false, module: null, ambiguous: false};
+        }
     }
 
-    // Step 3
+    // Step 4
     _DefineDataProperty(resolveSet, resolveSet.length, {module, exportName});
 
-    // Step 4
+    // Step 5
     let localExportEntries = module.localExportEntries;
     for (let i = 0; i < localExportEntries.length; i++) {
         let e = localExportEntries[i];
         if (exportName === e.exportName)
-            return {module, bindingName: e.localName};
+            return {resolved: true, module, bindingName: e.localName};
     }
 
-    // Step 5
+    // Step 6
     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_STATE_PARSED);
-            return callFunction(importedModule.resolveExport, importedModule, e.importName,
-                                resolveSet);
+                                                       MODULE_STATUS_UNINSTANTIATED);
+            let resolution = callFunction(importedModule.resolveExport, importedModule, e.importName,
+                                          resolveSet);
+            if (!resolution.resolved && !resolution.module)
+                resolution.module = module;
+            return resolution;
         }
     }
 
-    // Step 6
+    // Step 7
     if (exportName === "default") {
         // A default export cannot be provided by an export *.
-        return null;
+        return {resolved: false, module: null, ambiguous: false};
     }
 
-    // Step 7
+    // Step 8
     let starResolution = null;
 
-    // Step 8
+    // Step 9
     let starExportEntries = module.starExportEntries;
     for (let i = 0; i < starExportEntries.length; i++) {
         let e = starExportEntries[i];
         let importedModule = CallModuleResolveHook(module, e.moduleRequest,
-                                                   MODULE_STATE_PARSED);
-        let resolution = callFunction(importedModule.resolveExport, importedModule,
-                                      exportName, resolveSet);
-        if (resolution === "ambiguous")
+                                                   MODULE_STATUS_UNINSTANTIATED);
+        let resolution = callFunction(importedModule.resolveExport, importedModule, exportName,
+                                      resolveSet);
+        if (!resolution.resolved && (resolution.module || resolution.ambiguous))
             return resolution;
-
-        if (resolution !== null) {
+        if (resolution.resolved) {
             if (starResolution === null) {
                 starResolution = resolution;
             } else {
                 if (resolution.module !== starResolution.module ||
-                    resolution.exportName !== starResolution.exportName)
+                    resolution.bindingName !== starResolution.bindingName)
                 {
-                    return "ambiguous";
+                    return {resolved: false, module: null, ambiguous: true};
                 }
             }
         }
     }
 
-    // Step 9
-    return starResolution;
+    // Step 10
+    if (starResolution !== null)
+        return starResolution;
+
+    return {resolved: false, module: null, ambiguous: false};
 }
 
 // 15.2.1.18 GetModuleNamespace(module)
 function GetModuleNamespace(module)
 {
+    // Step 1
+    assert(IsModule(module), "GetModuleNamespace called with non-module");
+
     // Step 2
+    assert(module.status !== MODULE_STATUS_UNINSTANTIATED &&
+           module.status !== MODULE_STATUS_ERRORED,
+           "Bad module state in GetModuleNamespace");
+
+    // Step 3
     let namespace = module.namespace;
 
     // Step 3
     if (typeof namespace === "undefined") {
         let exportedNames = callFunction(module.getExportedNames, module);
         let unambiguousNames = [];
         for (let i = 0; i < exportedNames.length; i++) {
             let name = exportedNames[i];
             let resolution = callFunction(module.resolveExport, module, name);
-            if (resolution === null)
-                ThrowSyntaxError(JSMSG_MISSING_NAMESPACE_EXPORT);
-            if (resolution !== "ambiguous")
+            if (resolution.resolved)
                 _DefineDataProperty(unambiguousNames, unambiguousNames.length, name);
         }
         namespace = ModuleNamespaceCreate(module, unambiguousNames);
     }
 
     // Step 4
     return namespace;
 }
@@ -175,140 +221,424 @@ function ModuleNamespaceCreate(module, e
 
     let ns = NewModuleNamespace(module, exports);
 
     // Pre-compute all bindings now rather than calling ResolveExport() on every
     // access.
     for (let i = 0; i < exports.length; i++) {
         let name = exports[i];
         let binding = callFunction(module.resolveExport, module, name);
-        assert(binding !== null && binding !== "ambiguous", "Failed to resolve binding");
+        assert(binding.resolved, "Failed to resolve binding");
         AddModuleNamespaceBinding(ns, name, binding.module, binding.bindingName);
     }
 
     return ns;
 }
 
 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.state == MODULE_STATE_FAILED)
-        ThrowInternalError(JSMSG_MODULE_INSTANTIATE_FAILED);
+    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");
 
     return env;
 }
 
-function RecordInstantationFailure(module)
+function RecordModuleError(module, error)
 {
-    // Set the module's state to 'failed' to indicate a failed module
-    // instantiation and reset the environment slot to 'undefined'.
-    assert(IsModule(module), "Non-module passed to RecordInstantationFailure");
-    SetModuleState(module, MODULE_STATE_FAILED);
+    // 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);
 }
 
-// 15.2.1.16.4 ModuleDeclarationInstantiation()
-function ModuleDeclarationInstantiation()
+function CountArrayValues(array, value)
+{
+    let count = 0;
+    for (let i = 0; i < array.length; i++) {
+        if (array[i] === value)
+            count++;
+    }
+    return count;
+}
+
+function ArrayContains(array, value)
+{
+    for (let i = 0; i < array.length; i++) {
+        if (array[i] === value)
+            return true;
+    }
+    return false;
+}
+
+// 15.2.1.16.4 ModuleInstantiate()
+function ModuleInstantiate()
 {
     if (!IsObject(this) || !IsModule(this))
-        return callFunction(CallModuleMethodIfWrapped, this, "ModuleDeclarationInstantiation");
+        return callFunction(CallModuleMethodIfWrapped, this, "ModuleInstantiate");
 
     // Step 1
     let module = this;
 
-    // Step 5
-    if (GetModuleEnvironment(module) !== undefined)
-        return undefined;
+    // Step 2
+    if (module.status === MODULE_STATUS_INSTANTIATING ||
+        module.status === MODULE_STATUS_EVALUATING)
+    {
+        ThrowInternalError(JSMSG_BAD_MODULE_STATUS);
+    }
+
+    // Step 3
+    let stack = [];
+
+    // Steps 4-5
+    try {
+        InnerModuleDeclarationInstantiation(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);
+        }
+
+        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);
+        }
+
+        assert(module.status === MODULE_STATUS_ERRORED,
+               "Bad module status after failed instantiation");
+        assert(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT) === error,
+               "Module has different error set after failed instantiation");
+
+        throw error;
+    }
+
+    // Step 6
+    assert(module.status == MODULE_STATUS_INSTANTIATED ||
+           module.status == MODULE_STATUS_EVALUATED,
+           "Bad module status after successful instantiation");
 
     // Step 7
+    assert(stack.length === 0,
+           "Stack should be empty after successful instantiation");
+
+    // Step 8
+    return undefined;
+}
+_SetCanonicalName(ModuleInstantiate, "ModuleInstantiate");
+
+// 15.2.1.16.4.1 InnerModuleDeclarationInstantiation(module, stack, index)
+function InnerModuleDeclarationInstantiation(module, stack, index)
+{
+    // Step 1
+    // TODO: Support module records other than source text module records.
+
+    // Step 2
+    if (module.status === MODULE_STATUS_INSTANTIATING ||
+        module.status === MODULE_STATUS_INSTANTIATED ||
+        module.status === MODULE_STATUS_EVALUATED)
+    {
+        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
+    ModuleSetStatus(module, MODULE_STATUS_INSTANTIATING);
+
+    // Step 6-8
+    UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, index);
+    UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, index);
+    index++;
+
+    // Step 9
+    _DefineDataProperty(stack, stack.length, module);
+
+    // Step 10
+    let requestedModules = module.requestedModules;
+    for (let i = 0; i < requestedModules.length; i++) {
+        let required = requestedModules[i];
+        let requiredModule = CallModuleResolveHook(module, required, MODULE_STATUS_ERRORED);
+
+        index = InnerModuleDeclarationInstantiation(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");
+
+        assert((requiredModule.status === MODULE_STATUS_INSTANTIATING) ===
+               ArrayContains(stack, requiredModule),
+              "Required module should be in the stack iff it is currently being instantiated");
+
+        assert(typeof requiredModule.dfsIndex === "number", "Bad dfsIndex");
+        assert(typeof requiredModule.dfsAncestorIndex === "number", "Bad dfsAncestorIndex");
+
+        if (requiredModule.status === MODULE_STATUS_INSTANTIATING) {
+            UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT,
+                                  std_Math_min(module.dfsAncestorIndex,
+                                               requiredModule.dfsAncestorIndex));
+        }
+    }
+
+    // Step 11
+    ModuleDeclarationEnvironmentSetup(module);
+
+    // Steps 12-13
+    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
+    if (module.dfsAncestorIndex === module.dfsIndex) {
+        let requiredModule;
+        do {
+            requiredModule = callFunction(std_Array_pop, stack);
+            ModuleSetStatus(requiredModule, MODULE_STATUS_INSTANTIATED);
+        } while (requiredModule !== module);
+    }
+
+    // Step 15
+    return index;
+}
+
+// 15.2.1.16.4.2 ModuleDeclarationEnvironmentSetup(module)
+function ModuleDeclarationEnvironmentSetup(module)
+{
+    // Step 1
+    let indirectExportEntries = module.indirectExportEntries;
+    for (let i = 0; i < indirectExportEntries.length; i++) {
+        let e = indirectExportEntries[i];
+        let resolution = callFunction(module.resolveExport, module, e.exportName);
+        assert(resolution.resolved || resolution.module,
+               "Unexpected failure to resolve export in ModuleDeclarationEnvironmentSetup");
+        if (!resolution.resolved)
+            return ResolutionError(resolution, "indirectExport", e.exportName)
+    }
+
+    // Steps 5-6
     CreateModuleEnvironment(module);
     let env = GetModuleEnvironment(module);
 
-    SetModuleState(this, MODULE_STATE_INSTANTIATED);
+    // Step 8
+    let importEntries = module.importEntries;
+    for (let i = 0; i < importEntries.length; i++) {
+        let imp = importEntries[i];
+        let importedModule = CallModuleResolveHook(module, imp.moduleRequest,
+                                                   MODULE_STATUS_INSTANTIATING);
+        if (imp.importName === "*") {
+            let namespace = GetModuleNamespace(importedModule);
+            CreateNamespaceBinding(env, imp.localName, namespace);
+        } else {
+            let resolution = callFunction(importedModule.resolveExport, importedModule,
+                                          imp.importName);
+            if (!resolution.resolved && !resolution.module)
+                resolution.module = module;
+
+            if (!resolution.resolved)
+                return ResolutionError(resolution, "import", imp.importName);
+
+            CreateImportBinding(env, imp.localName, resolution.module, resolution.bindingName);
+        }
+    }
+
+    InstantiateModuleFunctionDeclarations(module);
+}
+
+// 15.2.1.16.4.3 ResolutionError(module)
+function ResolutionError(resolution, kind, name)
+{
+    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(kind === "import" || kind === "indirectExport",
+           "Unexpected kind in ResolutionError");
+
+    let message;
+    if (kind === "import") {
+        message = resolution.ambiguous ? JSMSG_AMBIGUOUS_IMPORT
+                                       : JSMSG_MISSING_IMPORT;
+    } else {
+        message = resolution.ambiguous ? JSMSG_AMBIGUOUS_INDIRECT_EXPORT
+                                       : JSMSG_MISSING_INDIRECT_EXPORT;
+    }
 
     try {
-        // Step 8
-        let requestedModules = module.requestedModules;
-        for (let i = 0; i < requestedModules.length; i++) {
-            let required = requestedModules[i];
-            let requiredModule = CallModuleResolveHook(module, required, MODULE_STATE_PARSED);
-            callFunction(requiredModule.declarationInstantiation, requiredModule);
-        }
-
-        // Step 9
-        let indirectExportEntries = module.indirectExportEntries;
-        for (let i = 0; i < indirectExportEntries.length; i++) {
-            let e = indirectExportEntries[i];
-            let resolution = callFunction(module.resolveExport, module, e.exportName);
-            if (resolution === null)
-                ThrowSyntaxError(JSMSG_MISSING_INDIRECT_EXPORT, e.exportName);
-            if (resolution === "ambiguous")
-                ThrowSyntaxError(JSMSG_AMBIGUOUS_INDIRECT_EXPORT, e.exportName);
-        }
-
-        // Step 12
-        let importEntries = module.importEntries;
-        for (let i = 0; i < importEntries.length; i++) {
-            let imp = importEntries[i];
-            let importedModule = CallModuleResolveHook(module, imp.moduleRequest,
-                                                       MODULE_STATE_INSTANTIATED);
-            if (imp.importName === "*") {
-                let namespace = GetModuleNamespace(importedModule);
-                CreateNamespaceBinding(env, imp.localName, namespace);
-            } else {
-                let resolution = callFunction(importedModule.resolveExport, importedModule,
-                                              imp.importName);
-                if (resolution === null)
-                    ThrowSyntaxError(JSMSG_MISSING_IMPORT, imp.importName);
-                if (resolution === "ambiguous")
-                    ThrowSyntaxError(JSMSG_AMBIGUOUS_IMPORT, imp.importName);
-                if (resolution.module.state < MODULE_STATE_INSTANTIATED)
-                    ThrowInternalError(JSMSG_BAD_MODULE_STATE);
-                CreateImportBinding(env, imp.localName, resolution.module, resolution.bindingName);
-            }
-        }
-
-        // Step 17.a.iii
-        InstantiateModuleFunctionDeclarations(module);
-    } catch (e) {
-        RecordInstantationFailure(module);
-        throw e;
+        ThrowSyntaxError(message, name);
+    } catch (error) {
+        RecordModuleError(module, error);
+        throw error;
     }
 }
-_SetCanonicalName(ModuleDeclarationInstantiation, "ModuleDeclarationInstantiation");
 
-// 15.2.1.16.5 ModuleEvaluation()
-function ModuleEvaluation()
+// 15.2.1.16.5 ModuleEvaluate()
+function ModuleEvaluate()
 {
     if (!IsObject(this) || !IsModule(this))
-        return callFunction(CallModuleMethodIfWrapped, this, "ModuleEvaluation");
+        return callFunction(CallModuleMethodIfWrapped, this, "ModuleEvaluate");
 
     // Step 1
     let module = this;
 
-    if (module.state < MODULE_STATE_INSTANTIATED)
-        ThrowInternalError(JSMSG_BAD_MODULE_STATE);
+    // Step 2
+    if (module.status !== MODULE_STATUS_ERRORED &&
+        module.status !== MODULE_STATUS_INSTANTIATED &&
+        module.status !== MODULE_STATUS_EVALUATED)
+    {
+        ThrowInternalError(JSMSG_BAD_MODULE_STATUS);
+    }
+
+    // Step 3
+    let stack = [];
+
+    // Steps 4-5
+    try {
+        InnerModuleEvaluation(module, stack, 0);
+    } catch (error) {
+        for (let i = 0; i < stack.length; i++) {
+            let m = stack[i];
+
+            assert(m.status === MODULE_STATUS_EVALUATING,
+                   "Bad module status after failed evaluation");
+
+            RecordModuleError(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);
+        }
+
+        assert(module.status === MODULE_STATUS_ERRORED,
+               "Bad module status after failed evaluation");
+        assert(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT) === error,
+               "Module has different error set after failed evaluation");
+
+        throw error;
+    }
+
+    assert(module.status == MODULE_STATUS_EVALUATED,
+           "Bad module status after successful evaluation");
+    assert(stack.length === 0,
+           "Stack should be empty after successful evaluation");
+
+    return undefined;
+}
+_SetCanonicalName(ModuleEvaluate, "ModuleEvaluate");
+
+// 15.2.1.16.5.1 InnerModuleEvaluation(module, stack, index)
+function InnerModuleEvaluation(module, stack, index)
+{
+    // Step 1
+    // TODO: Support module records other than source text module records.
+
+    // Step 2
+    if (module.status === MODULE_STATUS_EVALUATING ||
+        module.status === MODULE_STATUS_EVALUATED)
+    {
+        return index;
+    }
+
+    // Step 3
+    if (module.status === MODULE_STATUS_ERRORED)
+        throw module.error;
 
     // Step 4
-    if (module.state == MODULE_STATE_EVALUATED)
-        return undefined;
+    assert(module.status === MODULE_STATUS_INSTANTIATED,
+          "Bad module status in ModuleEvaluation");
 
     // Step 5
-    SetModuleState(this, MODULE_STATE_EVALUATED);
+    ModuleSetStatus(module, MODULE_STATUS_EVALUATING);
 
-    // Step 6
+    // Steps 6-8
+    UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, index);
+    UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, index);
+    index++;
+
+    // Step 9
+    _DefineDataProperty(stack, stack.length, module);
+
+    // Step 10
     let requestedModules = module.requestedModules;
     for (let i = 0; i < requestedModules.length; i++) {
         let required = requestedModules[i];
-        let requiredModule = CallModuleResolveHook(module, required, MODULE_STATE_INSTANTIATED);
-        callFunction(requiredModule.evaluation, requiredModule);
+        let requiredModule =
+            CallModuleResolveHook(module, required, MODULE_STATUS_INSTANTIATED);
+
+        index = InnerModuleEvaluation(requiredModule, stack, index);
+
+        assert(requiredModule.status == MODULE_STATUS_EVALUATING ||
+               requiredModule.status == MODULE_STATUS_EVALUATED,
+              "Bad module status after InnerModuleEvaluation");
+
+        assert((requiredModule.status === MODULE_STATUS_EVALUATING) ===
+               ArrayContains(stack, requiredModule),
+               "Required module should be in the stack iff it is currently being evaluated");
+
+        assert(typeof requiredModule.dfsIndex === "number", "Bad dfsIndex");
+        assert(typeof requiredModule.dfsAncestorIndex === "number", "Bad dfsAncestorIndex");
+
+        if (requiredModule.status === MODULE_STATUS_EVALUATING) {
+            UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT,
+                                  std_Math_min(module.dfsAncestorIndex,
+                                               requiredModule.dfsAncestorIndex));
+        }
     }
 
-    return EvaluateModule(module);
+    // Step 11
+    ExecuteModule(module);
+
+    // Step 12
+    assert(CountArrayValues(stack, module) === 1,
+           "Current module should appear exactly once in the stack");
+
+    // Step 13
+    assert(module.dfsAncestorIndex <= module.dfsIndex,
+           "Bad DFS ancestor index");
+
+    // Step 14
+    if (module.dfsAncestorIndex === module.dfsIndex) {
+        let requiredModule;
+        do {
+            requiredModule = callFunction(std_Array_pop, stack);
+            ModuleSetStatus(requiredModule, MODULE_STATUS_EVALUATED);
+        } while (requiredModule !== module);
+    }
+
+    // Step 15
+    return index;
 }
-_SetCanonicalName(ModuleEvaluation, "ModuleEvaluation");
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -15,20 +15,21 @@
 #include "vm/AsyncIteration.h"
 
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 using namespace js;
 using namespace js::frontend;
 
-static_assert(MODULE_STATE_FAILED < MODULE_STATE_PARSED &&
-              MODULE_STATE_PARSED < MODULE_STATE_INSTANTIATED &&
-              MODULE_STATE_INSTANTIATED < MODULE_STATE_EVALUATED,
-              "Module states are ordered incorrectly");
+static_assert(MODULE_STATUS_ERRORED < MODULE_STATUS_UNINSTANTIATED &&
+              MODULE_STATUS_UNINSTANTIATED < MODULE_STATUS_INSTANTIATING &&
+              MODULE_STATUS_INSTANTIATING < MODULE_STATUS_INSTANTIATED &&
+              MODULE_STATUS_INSTANTIATED < MODULE_STATUS_EVALUATED,
+              "Module statuses are ordered incorrectly");
 
 template<typename T, Value ValueGetter(const T* obj)>
 static bool
 ModuleValueGetterImpl(JSContext* cx, const CallArgs& args)
 {
     args.rval().set(ValueGetter(&args.thisv().toObject().as<T>()));
     return true;
 }
@@ -39,17 +40,17 @@ ModuleValueGetter(JSContext* cx, unsigne
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<T::isInstance, ModuleValueGetterImpl<T, ValueGetter>>(cx, args);
 }
 
 #define DEFINE_GETTER_FUNCTIONS(cls, name, slot)                              \
     static Value                                                              \
     cls##_##name##Value(const cls* obj) {                                     \
-        return obj->getFixedSlot(cls::slot);                                  \
+        return obj->getReservedSlot(cls::slot);                               \
     }                                                                         \
                                                                               \
     static bool                                                               \
     cls##_##name##Getter(JSContext* cx, unsigned argc, Value* vp)             \
     {                                                                         \
         return ModuleValueGetter<cls, cls##_##name##Value>(cx, argc, vp);     \
     }
 
@@ -566,17 +567,17 @@ ModuleObject::class_ = {
     JSCLASS_BACKGROUND_FINALIZE,
     &ModuleObject::classOps_
 };
 
 #define DEFINE_ARRAY_SLOT_ACCESSOR(cls, name, slot)                           \
     ArrayObject&                                                              \
     cls::name() const                                                         \
     {                                                                         \
-        return getFixedSlot(cls::slot).toObject().as<ArrayObject>();          \
+        return getReservedSlot(cls::slot).toObject().as<ArrayObject>();       \
     }
 
 DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, requestedModules, RequestedModulesSlot)
 DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, importEntries, ImportEntriesSlot)
 DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, localExportEntries, LocalExportEntriesSlot)
 DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, indirectExportEntries, IndirectExportEntriesSlot)
 DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, starExportEntries, StarExportEntriesSlot)
 
@@ -627,16 +628,18 @@ ModuleObject::finalize(js::FreeOp* fop, 
         fop->delete_(bindings);
     if (FunctionDeclarationVector* funDecls = self->functionDeclarations())
         fop->delete_(funDecls);
 }
 
 ModuleEnvironmentObject*
 ModuleObject::environment() const
 {
+    MOZ_ASSERT(status() != MODULE_STATUS_ERRORED);
+
     Value value = getReservedSlot(EnvironmentSlot);
     if (value.isUndefined())
         return nullptr;
 
     return &value.toObject().as<ModuleEnvironmentObject>();
 }
 
 bool
@@ -690,17 +693,17 @@ ModuleObject::functionDeclarations()
 
     return static_cast<FunctionDeclarationVector*>(value.toPrivate());
 }
 
 void
 ModuleObject::init(HandleScript script)
 {
     initReservedSlot(ScriptSlot, PrivateValue(script));
-    initReservedSlot(StateSlot, Int32Value(MODULE_STATE_FAILED));
+    initReservedSlot(StatusSlot, Int32Value(MODULE_STATUS_ERRORED));
 }
 
 void
 ModuleObject::setInitialEnvironment(HandleModuleEnvironmentObject initialEnvironment)
 {
     initReservedSlot(InitialEnvironmentSlot, ObjectValue(*initialEnvironment));
 }
 
@@ -711,17 +714,17 @@ ModuleObject::initImportExportData(Handl
                                    HandleArrayObject indirectExportEntries,
                                    HandleArrayObject starExportEntries)
 {
     initReservedSlot(RequestedModulesSlot, ObjectValue(*requestedModules));
     initReservedSlot(ImportEntriesSlot, ObjectValue(*importEntries));
     initReservedSlot(LocalExportEntriesSlot, ObjectValue(*localExportEntries));
     initReservedSlot(IndirectExportEntriesSlot, ObjectValue(*indirectExportEntries));
     initReservedSlot(StarExportEntriesSlot, ObjectValue(*starExportEntries));
-    setReservedSlot(StateSlot, Int32Value(MODULE_STATE_PARSED));
+    setReservedSlot(StatusSlot, Int32Value(MODULE_STATUS_UNINSTANTIATED));
 }
 
 static bool
 FreezeObjectProperty(JSContext* cx, HandleNativeObject obj, uint32_t slot)
 {
     RootedObject property(cx, &obj->getSlot(slot).toObject());
     return FreezeObject(cx, property);
 }
@@ -792,27 +795,34 @@ ModuleObject::hasScript() const
 
 JSScript*
 ModuleObject::script() const
 {
     return static_cast<JSScript*>(getReservedSlot(ScriptSlot).toPrivate());
 }
 
 static inline void
-AssertValidModuleState(ModuleState state)
+AssertValidModuleStatus(ModuleStatus status)
 {
-    MOZ_ASSERT(state >= MODULE_STATE_FAILED && state <= MODULE_STATE_EVALUATED);
+    MOZ_ASSERT(status >= MODULE_STATUS_ERRORED && status <= MODULE_STATUS_EVALUATED);
 }
 
-ModuleState
-ModuleObject::state() const
+ModuleStatus
+ModuleObject::status() const
 {
-    ModuleState state = getReservedSlot(StateSlot).toInt32();
-    AssertValidModuleState(state);
-    return state;
+    ModuleStatus status = getReservedSlot(StatusSlot).toInt32();
+    AssertValidModuleStatus(status);
+    return status;
+}
+
+Value
+ModuleObject::error() const
+{
+    MOZ_ASSERT(status() == MODULE_STATUS_ERRORED);
+    return getReservedSlot(ErrorSlot);
 }
 
 Value
 ModuleObject::hostDefinedField() const
 {
     return getReservedSlot(HostDefinedSlot);
 }
 
@@ -910,27 +920,18 @@ ModuleObject::instantiateFunctionDeclara
             return false;
     }
 
     js_delete(funDecls);
     self->setReservedSlot(FunctionDeclarationsSlot, UndefinedValue());
     return true;
 }
 
-void
-ModuleObject::setState(int32_t newState)
-{
-    AssertValidModuleState(newState);
-    MOZ_ASSERT(state() != MODULE_STATE_FAILED);
-    MOZ_ASSERT(newState == MODULE_STATE_FAILED || newState > state());
-    setReservedSlot(StateSlot, Int32Value(newState));
-}
-
 /* static */ bool
-ModuleObject::evaluate(JSContext* cx, HandleModuleObject self, MutableHandleValue rval)
+ModuleObject::execute(JSContext* cx, HandleModuleObject self, MutableHandleValue rval)
 {
     MOZ_ASSERT(IsFrozen(cx, self));
 
     RootedScript script(cx, self->script());
     RootedModuleEnvironmentObject scope(cx, self->environment());
     if (!scope) {
         JS_ReportErrorASCII(cx, "Module declarations have not yet been instantiated");
         return false;
@@ -970,54 +971,60 @@ InvokeSelfHostedMethod(JSContext* cx, Ha
     if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), name, name, 0, &fval))
         return false;
 
     RootedValue ignored(cx);
     return Call(cx, fval, self, &ignored);
 }
 
 /* static */ bool
-ModuleObject::DeclarationInstantiation(JSContext* cx, HandleModuleObject self)
+ModuleObject::Instantiate(JSContext* cx, HandleModuleObject self)
 {
-    return InvokeSelfHostedMethod(cx, self, cx->names().ModuleDeclarationInstantiation);
+    return InvokeSelfHostedMethod(cx, self, cx->names().ModuleInstantiate);
 }
 
 /* static */ bool
-ModuleObject::Evaluation(JSContext* cx, HandleModuleObject self)
+ModuleObject::Evaluate(JSContext* cx, HandleModuleObject self)
 {
-    return InvokeSelfHostedMethod(cx, self, cx->names().ModuleEvaluation);
+    return InvokeSelfHostedMethod(cx, self, cx->names().ModuleEvaluate);
 }
 
 DEFINE_GETTER_FUNCTIONS(ModuleObject, namespace_, NamespaceSlot)
-DEFINE_GETTER_FUNCTIONS(ModuleObject, state, StateSlot)
+DEFINE_GETTER_FUNCTIONS(ModuleObject, status, StatusSlot)
+DEFINE_GETTER_FUNCTIONS(ModuleObject, error, ErrorSlot)
 DEFINE_GETTER_FUNCTIONS(ModuleObject, requestedModules, RequestedModulesSlot)
 DEFINE_GETTER_FUNCTIONS(ModuleObject, importEntries, ImportEntriesSlot)
 DEFINE_GETTER_FUNCTIONS(ModuleObject, localExportEntries, LocalExportEntriesSlot)
 DEFINE_GETTER_FUNCTIONS(ModuleObject, indirectExportEntries, IndirectExportEntriesSlot)
 DEFINE_GETTER_FUNCTIONS(ModuleObject, starExportEntries, StarExportEntriesSlot)
+DEFINE_GETTER_FUNCTIONS(ModuleObject, dfsIndex, DFSIndexSlot)
+DEFINE_GETTER_FUNCTIONS(ModuleObject, dfsAncestorIndex, DFSAncestorIndexSlot)
 
 /* static */ bool
 GlobalObject::initModuleProto(JSContext* cx, Handle<GlobalObject*> global)
 {
     static const JSPropertySpec protoAccessors[] = {
         JS_PSG("namespace", ModuleObject_namespace_Getter, 0),
-        JS_PSG("state", ModuleObject_stateGetter, 0),
+        JS_PSG("status", ModuleObject_statusGetter, 0),
+        JS_PSG("error", ModuleObject_errorGetter, 0),
         JS_PSG("requestedModules", ModuleObject_requestedModulesGetter, 0),
         JS_PSG("importEntries", ModuleObject_importEntriesGetter, 0),
         JS_PSG("localExportEntries", ModuleObject_localExportEntriesGetter, 0),
         JS_PSG("indirectExportEntries", ModuleObject_indirectExportEntriesGetter, 0),
         JS_PSG("starExportEntries", ModuleObject_starExportEntriesGetter, 0),
+        JS_PSG("dfsIndex", ModuleObject_dfsIndexGetter, 0),
+        JS_PSG("dfsAncestorIndex", ModuleObject_dfsAncestorIndexGetter, 0),
         JS_PS_END
     };
 
     static const JSFunctionSpec protoFunctions[] = {
         JS_SELF_HOSTED_FN("getExportedNames", "ModuleGetExportedNames", 1, 0),
         JS_SELF_HOSTED_FN("resolveExport", "ModuleResolveExport", 2, 0),
-        JS_SELF_HOSTED_FN("declarationInstantiation", "ModuleDeclarationInstantiation", 0, 0),
-        JS_SELF_HOSTED_FN("evaluation", "ModuleEvaluation", 0, 0),
+        JS_SELF_HOSTED_FN("declarationInstantiation", "ModuleInstantiate", 0, 0),
+        JS_SELF_HOSTED_FN("evaluation", "ModuleEvaluate", 0, 0),
         JS_FS_END
     };
 
     RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
     if (!proto)
         return false;
 
     if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, protoFunctions))
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -187,44 +187,55 @@ struct FunctionDeclaration
     void trace(JSTracer* trc);
 
     HeapPtr<JSAtom*> name;
     HeapPtr<JSFunction*> fun;
 };
 
 using FunctionDeclarationVector = GCVector<FunctionDeclaration, 0, ZoneAllocPolicy>;
 
-// Possible values for ModuleState are defined in SelfHostingDefines.h.
-using ModuleState = int32_t;
+// Possible values for ModuleStatus are defined in SelfHostingDefines.h.
+using ModuleStatus = int32_t;
 
 class ModuleObject : public NativeObject
 {
   public:
     enum
     {
         ScriptSlot = 0,
         InitialEnvironmentSlot,
         EnvironmentSlot,
         NamespaceSlot,
-        StateSlot,
+        StatusSlot,
+        ErrorSlot,
         HostDefinedSlot,
         RequestedModulesSlot,
         ImportEntriesSlot,
         LocalExportEntriesSlot,
         IndirectExportEntriesSlot,
         StarExportEntriesSlot,
         ImportBindingsSlot,
         NamespaceExportsSlot,
         NamespaceBindingsSlot,
         FunctionDeclarationsSlot,
+        DFSIndexSlot,
+        DFSAncestorIndexSlot,
         SlotCount
     };
 
     static_assert(EnvironmentSlot == MODULE_OBJECT_ENVIRONMENT_SLOT,
                   "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(DFSIndexSlot == MODULE_OBJECT_DFS_INDEX_SLOT,
+                  "DFSIndexSlot must match self-hosting define");
+    static_assert(DFSAncestorIndexSlot == MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT,
+                  "DFSAncestorIndexSlot must match self-hosting define");
 
     static const Class class_;
 
     static bool isInstance(HandleValue value);
 
     static ModuleObject* create(JSContext* cx);
     void init(HandleScript script);
     void setInitialEnvironment(Handle<ModuleEnvironmentObject*> initialEnvironment);
@@ -239,45 +250,44 @@ class ModuleObject : public NativeObject
 #endif
     void fixEnvironmentsAfterCompartmentMerge();
 
     JSScript* script() const;
     Scope* enclosingScope() const;
     ModuleEnvironmentObject& initialEnvironment() const;
     ModuleEnvironmentObject* environment() const;
     ModuleNamespaceObject* namespace_();
-    ModuleState state() const;
+    ModuleStatus status() const;
+    Value error() const;
     Value hostDefinedField() const;
     ArrayObject& requestedModules() const;
     ArrayObject& importEntries() const;
     ArrayObject& localExportEntries() const;
     ArrayObject& indirectExportEntries() const;
     ArrayObject& starExportEntries() const;
     IndirectBindingMap& importBindings();
     JSObject* namespaceExports();
     IndirectBindingMap* namespaceBindings();
 
-    static bool DeclarationInstantiation(JSContext* cx, HandleModuleObject self);
-    static bool Evaluation(JSContext* cx, HandleModuleObject self);
+    static bool Instantiate(JSContext* cx, HandleModuleObject self);
+    static bool Evaluate(JSContext* cx, HandleModuleObject self);
 
     void setHostDefinedField(const JS::Value& value);
 
     // For intrinsic_CreateModuleEnvironment.
     void createEnvironment();
 
     // For BytecodeEmitter.
     bool noteFunctionDeclaration(JSContext* cx, HandleAtom name, HandleFunction fun);
 
     // For intrinsic_InstantiateModuleFunctionDeclarations.
     static bool instantiateFunctionDeclarations(JSContext* cx, HandleModuleObject self);
 
-    void setState(ModuleState newState);
-
-    // For intrinsic_EvaluateModule.
-    static bool evaluate(JSContext* cx, HandleModuleObject self, MutableHandleValue rval);
+    // For intrinsic_ExecuteModule.
+    static bool execute(JSContext* cx, HandleModuleObject self, MutableHandleValue rval);
 
     // For intrinsic_NewModuleNamespace.
     static ModuleNamespaceObject* createNamespace(JSContext* cx, HandleModuleObject self,
                                                   HandleObject exports);
 
   private:
     static const ClassOps classOps_;
 
--- a/js/src/builtin/SelfHostingDefines.h
+++ b/js/src/builtin/SelfHostingDefines.h
@@ -88,22 +88,28 @@
 #define REGEXP_FLAGS_SLOT 2
 
 #define REGEXP_IGNORECASE_FLAG  0x01
 #define REGEXP_GLOBAL_FLAG      0x02
 #define REGEXP_MULTILINE_FLAG   0x04
 #define REGEXP_STICKY_FLAG      0x08
 #define REGEXP_UNICODE_FLAG     0x10
 
-#define MODULE_OBJECT_ENVIRONMENT_SLOT 2
+#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_STATE_FAILED       0
-#define MODULE_STATE_PARSED       1
-#define MODULE_STATE_INSTANTIATED 2
-#define MODULE_STATE_EVALUATED    3
+#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 STRING_GENERICS_CHAR_AT               0
 #define STRING_GENERICS_CHAR_CODE_AT          1
 #define STRING_GENERICS_CONCAT                2
 #define STRING_GENERICS_ENDS_WITH             3
 #define STRING_GENERICS_INCLUDES              4
 #define STRING_GENERICS_INDEX_OF              5
 #define STRING_GENERICS_LAST_INDEX_OF         6
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -23,23 +23,30 @@
 */
 
 #include "SelfHostingDefines.h"
 #include "TypedObjectConstants.h"
 
 // Assertions and debug printing, defined here instead of in the header above
 // to make `assert` invisible to C++.
 #ifdef DEBUG
-#define assert(b, info) if (!(b)) AssertionFailed(__FILE__ + ":" + __LINE__ + ": " + info)
-#define dbg(msg) DumpMessage(callFunction(std_Array_pop, \
-                                          StringSplitString(__FILE__, '/')) \
-                             + '#' + __LINE__ + ': ' + msg)
+#define assert(b, info) \
+    do { \
+        if (!(b)) \
+            AssertionFailed(__FILE__ + ":" + __LINE__ + ": " + info) \
+    } while (false)
+#define dbg(msg) \
+    do { \
+        DumpMessage(callFunction(std_Array_pop, \
+                                 StringSplitString(__FILE__, '/')) + \
+                    '#' + __LINE__ + ': ' + msg) \
+    } while (false)
 #else
-#define assert(b, info) // Elided assertion.
-#define dbg(msg) // Elided debugging output.
+#define assert(b, info) do {} while (false) // Elided assertion.
+#define dbg(msg) do {} while (false) // Elided debugging output.
 #endif
 
 // All C++-implemented standard builtins library functions used in self-hosted
 // code are installed via the std_functions JSFunctionSpec[] in
 // SelfHosting.cpp.
 //
 // Do not create an alias to a self-hosted builtin, otherwise it will be cloned
 // twice.
--- a/js/src/jit-test/tests/modules/ambiguous-star-export.js
+++ b/js/src/jit-test/tests/modules/ambiguous-star-export.js
@@ -1,41 +1,44 @@
 // Test ambigious export * statements.
 
 "use strict";
 
 load(libdir + "asserts.js");
 load(libdir + "dummyModuleResolveHook.js");
 
-function checkModuleEval(source, result) {
+function checkModuleEval(source) {
     let m = parseModule(source);
     m.declarationInstantiation();
-    assertEq(m.evaluation(), result);
+    m.evaluation();
+    return m;
 }
 
 function checkModuleSyntaxError(source) {
     let m = parseModule(source);
     assertThrowsInstanceOf(() => m.declarationInstantiation(), SyntaxError);
 }
 
 let a = moduleRepo['a'] = parseModule("export var a = 1; export var b = 2;");
 let b = moduleRepo['b'] = parseModule("export var b = 3; export var c = 4;");
 let c = moduleRepo['c'] = parseModule("export * from 'a'; export * from 'b';");
 c.declarationInstantiation();
 c.evaluation();
 
 // Check importing/exporting non-ambiguous name works.
-checkModuleEval("import { a } from 'c'; a;", 1);
-checkModuleEval("export { a } from 'c';", undefined);
+let d = checkModuleEval("import { a } from 'c';");
+assertEq(getModuleEnvironmentValue(d, "a"), 1);
+checkModuleEval("export { a } from 'c';");
 
 // Check importing/exporting ambiguous name is a syntax error.
 checkModuleSyntaxError("import { b } from 'c';");
 checkModuleSyntaxError("export { b } from 'c';");
 
 // Check that namespace objects include only non-ambiguous names.
-let m = parseModule("import * as ns from 'c'; ns;");
+let m = parseModule("import * as ns from 'c';");
 m.declarationInstantiation();
-let ns = m.evaluation();
+m.evaluation();
+let ns = c.namespace;
 let names = Object.keys(ns);
 assertEq(names.length, 2);
 assertEq('a' in ns, true);
 assertEq('b' in ns, false);
 assertEq('c' in ns, true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/modules/bad-namespace-created.js
@@ -0,0 +1,17 @@
+// Prior to https://github.com/tc39/ecma262/pull/916 it was possible for a
+// module namespace object to be successfully created that was later found to be
+// erroneous. Test that this is no longer the case.
+
+"use strict";
+
+load(libdir + "asserts.js");
+load(libdir + "dummyModuleResolveHook.js");
+
+moduleRepo['A'] = parseModule('import "B"; export {x} from "C"');
+moduleRepo['B'] = parseModule('import * as a from "A"');
+moduleRepo['C'] = parseModule('export * from "D"; export * from "E"');
+moduleRepo['D'] = parseModule('export let x');
+moduleRepo['E'] = parseModule('export let x');
+
+let m = moduleRepo['A'];
+assertThrowsInstanceOf(() => m.declarationInstantiation(), SyntaxError);
--- a/js/src/jit-test/tests/modules/bug-1284486-2.js
+++ b/js/src/jit-test/tests/modules/bug-1284486-2.js
@@ -1,24 +1,35 @@
-// |jit-test| error: InternalError
-
 // This tests that attempting to perform ModuleDeclarationInstantation a second
-// time after a failure throws an error. Doing this would be a bug in the module
-// loader, which is expected to throw away modules if there is an error
-// instantiating them.
+// time after a failure re-throws the same error.
 //
 // The first attempt fails becuase module 'a' is not available. The second
 // attempt fails because of the previous failure.
 //
 // This test exercises the path where the previously instantiated module is
 // re-instantiated directly.
 
-let moduleRepo = {};
-setModuleResolveHook(function(module, specifier) {
-    return moduleRepo[specifier];
-});
-let c;
+load(libdir + "dummyModuleResolveHook.js");
+
+let b = moduleRepo['b'] = parseModule("export var b = 3; export var c = 4;");
+let c = moduleRepo['c'] = parseModule("export * from 'a'; export * from 'b';");
+
+let e1;
+let threw = false;
 try {
-    let b = moduleRepo['b'] = parseModule("export var b = 3; export var c = 4;");
-    c = moduleRepo['c'] = parseModule("export * from 'a'; export * from 'b';");
     c.declarationInstantiation();
-} catch (exc) {}
-c.declarationInstantiation();
+} catch (exc) {
+    threw = true;
+    e1 = exc;
+}
+assertEq(threw, true);
+assertEq(typeof e1 === "undefined", false);
+
+threw = false;
+let e2;
+try {
+    c.declarationInstantiation();
+} catch (exc) {
+    threw = true;
+    e2 = exc;
+}
+assertEq(threw, true);
+assertEq(e1, e2);
--- a/js/src/jit-test/tests/modules/bug-1284486.js
+++ b/js/src/jit-test/tests/modules/bug-1284486.js
@@ -1,26 +1,39 @@
-// |jit-test| error: InternalError
-
 // This tests that attempting to perform ModuleDeclarationInstantation a second
-// time after a failure throws an error. Doing this would be a bug in the module
-// loader, which is expected to throw away modules if there is an error
-// instantiating them.
+// time after a failure re-throws the same error.
 //
 // 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).
 //
 // This test exercises the path where the previously instantiated module is
 // encountered as an import.
 
-let moduleRepo = {};
-setModuleResolveHook(function(module, specifier) {
-    return moduleRepo[specifier];
-});
+load(libdir + "dummyModuleResolveHook.js");
+
+let b = moduleRepo['b'] = parseModule("export var b = 3; export var c = 4;");
+let c = moduleRepo['c'] = parseModule("export * from 'a'; export * from 'b';");
+
+let e1;
+let threw = false;
 try {
-    let b = moduleRepo['b'] = parseModule("export var b = 3; export var c = 4;");
-    let c = moduleRepo['c'] = parseModule("export * from 'a'; export * from 'b';");
     c.declarationInstantiation();
-} catch (exc) {}
+} catch (exc) {
+    threw = true;
+    e1 = exc;
+}
+assertEq(threw, true);
+assertEq(typeof e1 === "undefined", false);
+
 let a = moduleRepo['a'] = parseModule("export var a = 1; export var b = 2;");
 let d = moduleRepo['d'] = parseModule("import { a } from 'c'; a;");
-d.declarationInstantiation();
+
+threw = false;
+let e2;
+try {
+    d.declarationInstantiation();
+} catch (exc) {
+    threw = true;
+    e2 = exc;
+}
+assertEq(threw, true);
+assertEq(e1, e2);
--- a/js/src/jit-test/tests/modules/bug-1287410.js
+++ b/js/src/jit-test/tests/modules/bug-1287410.js
@@ -15,8 +15,9 @@ c.declarationInstantiation();
 // This should not happen and would be a bug in the module loader.
 a = moduleRepo['a'] = parseModule("export var a = 1; export var b = 2;");
 
 let d = moduleRepo['d'] = parseModule("import { a } from 'c'; a;");
 
 // Attempting to instantiate 'd' throws an error because depdency 'a' of
 // instantiated module 'c' is not instantiated.
 d.declarationInstantiation();
+d.evaluation();
--- a/js/src/jit-test/tests/modules/global-scope.js
+++ b/js/src/jit-test/tests/modules/global-scope.js
@@ -1,32 +1,34 @@
 // Test interaction with global object and global lexical scope.
 
-function parseAndEvaluate(source) {
+function evalModuleAndCheck(source, expected) {
     let m = parseModule(source);
     m.declarationInstantiation();
-    return m.evaluation();
+    m.evaluation();
+    assertEq(getModuleEnvironmentValue(m, "r"), expected);
 }
 
 var x = 1;
-assertEq(parseAndEvaluate("let r = x; x = 2; r"), 1);
+evalModuleAndCheck("export let r = x; x = 2;", 1);
 assertEq(x, 2);
 
 let y = 3;
-assertEq(parseAndEvaluate("let r = y; y = 4; r"), 3);
+evalModuleAndCheck("export let r = y; y = 4;", 3);
 assertEq(y, 4);
 
 if (helperThreadCount() == 0)
     quit();
 
-function offThreadParseAndEvaluate(source) {
+function offThreadEvalModuleAndCheck(source, expected) {
     offThreadCompileModule(source);
     let m = finishOffThreadModule();
     print("compiled");
     m.declarationInstantiation();
-    return m.evaluation();
+    m.evaluation();
+    assertEq(getModuleEnvironmentValue(m, "r"), expected);
 }
 
-assertEq(offThreadParseAndEvaluate("let r = x; x = 5; r"), 2);
+offThreadEvalModuleAndCheck("export let r = x; x = 5;", 2);
 assertEq(x, 5);
 
-assertEq(offThreadParseAndEvaluate("let r = y; y = 6; r"), 4);
+offThreadEvalModuleAndCheck("export let r = y; y = 6;", 4);
 assertEq(y, 6);
--- a/js/src/jit-test/tests/modules/module-evaluation.js
+++ b/js/src/jit-test/tests/modules/module-evaluation.js
@@ -1,26 +1,27 @@
 // Exercise ModuleEvaluation() concrete method.
 
 load(libdir + "asserts.js");
 load(libdir + "dummyModuleResolveHook.js");
 
 function parseAndEvaluate(source) {
     let m = parseModule(source);
     m.declarationInstantiation();
-    return m.evaluation();
+    m.evaluation();
+    return m;
 }
 
 // Check the evaluation of an empty module succeeds.
-assertEq(typeof parseAndEvaluate(""), "undefined");
+parseAndEvaluate("");
 
 // Check evaluation returns evaluation result the first time, then undefined.
 let m = parseModule("1");
 m.declarationInstantiation();
-assertEq(m.evaluation(), 1);
+assertEq(m.evaluation(), undefined);
 assertEq(typeof m.evaluation(), "undefined");
 
 // Check top level variables are initialized by evaluation.
 m = parseModule("export var x = 2 + 2;");
 assertEq(typeof getModuleEnvironmentValue(m, "x"), "undefined");
 m.declarationInstantiation();
 m.evaluation();
 assertEq(getModuleEnvironmentValue(m, "x"), 4);
@@ -55,40 +56,44 @@ parseAndEvaluate("export default functio
 parseAndEvaluate("import a from 'a';");
 parseAndEvaluate("import { x } from 'a';");
 parseAndEvaluate("import * as ns from 'a';");
 parseAndEvaluate("export * from 'a'");
 parseAndEvaluate("export default class { constructor() {} };");
 parseAndEvaluate("export default class foo { constructor() {} };");
 
 // Test default import
-m = parseModule("import a from 'a'; a;")
+m = parseModule("import a from 'a'; export { a };")
 m.declarationInstantiation();
-assertEq(m.evaluation(), 2);
+m.evaluation()
+assertEq(getModuleEnvironmentValue(m, "a"), 2);
 
 // Test named import
-m = parseModule("import { x as y } from 'a'; y;")
+m = parseModule("import { x as y } from 'a'; export { y };")
 m.declarationInstantiation();
-assertEq(m.evaluation(), 1);
+m.evaluation();
+assertEq(getModuleEnvironmentValue(m, "y"), 1);
 
 // Call exported function
-m = parseModule("import { f } from 'a'; f(3);")
+m = parseModule("import { f } from 'a'; export let x = f(3);")
 m.declarationInstantiation();
-assertEq(m.evaluation(), 4);
+m.evaluation();
+assertEq(getModuleEnvironmentValue(m, "x"), 4);
 
 // Test importing an indirect export
 moduleRepo['b'] = parseModule("export { x as z } from 'a';");
-assertEq(parseAndEvaluate("import { z } from 'b'; z"), 1);
+m = parseAndEvaluate("import { z } from 'b'; export { z }");
+assertEq(getModuleEnvironmentValue(m, "z"), 1);
 
 // Test cyclic dependencies
 moduleRepo['c1'] = parseModule("export var x = 1; export {y} from 'c2'");
 moduleRepo['c2'] = parseModule("export var y = 2; export {x} from 'c1'");
-assertDeepEq(parseAndEvaluate(`import { x as x1, y as y1 } from 'c1';
-                               import { x as x2, y as y2 } from 'c2';
-                               [x1, y1, x2, y2]`),
-             [1, 2, 1, 2]);
+m = parseAndEvaluate(`import { x as x1, y as y1 } from 'c1';
+                      import { x as x2, y as y2 } from 'c2';
+                      export let z = [x1, y1, x2, y2]`),
+assertDeepEq(getModuleEnvironmentValue(m, "z"), [1, 2, 1, 2]);
 
 // Import access in functions
 m = parseModule("import { x } from 'a'; function f() { return x; }")
 m.declarationInstantiation();
 m.evaluation();
 let f = getModuleEnvironmentValue(m, "f");
 assertEq(f(), 1);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -581,17 +581,17 @@ MSG_DEF(JSMSG_REINIT_THIS,       0, JSEX
 MSG_DEF(JSMSG_BAD_DEFAULT_EXPORT,        0, JSEXN_SYNTAXERR, "default export cannot be provided by export *")
 MSG_DEF(JSMSG_MISSING_INDIRECT_EXPORT,   1, JSEXN_SYNTAXERR, "indirect export '{0}' not found")
 MSG_DEF(JSMSG_AMBIGUOUS_INDIRECT_EXPORT, 1, JSEXN_SYNTAXERR, "ambiguous indirect export '{0}'")
 MSG_DEF(JSMSG_MISSING_IMPORT,            1, JSEXN_SYNTAXERR, "import '{0}' not found")
 MSG_DEF(JSMSG_AMBIGUOUS_IMPORT,          1, JSEXN_SYNTAXERR, "ambiguous import '{0}'")
 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_STATE,          0, JSEXN_INTERNALERR, "module record in unexpected state")
+MSG_DEF(JSMSG_BAD_MODULE_STATUS,         0, JSEXN_INTERNALERR, "module record has unexpected status")
 
 // Promise
 MSG_DEF(JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF,       0, JSEXN_TYPEERR, "A promise cannot be resolved with itself.")
 MSG_DEF(JSMSG_PROMISE_CAPABILITY_HAS_SOMETHING_ALREADY, 0, JSEXN_TYPEERR, "GetCapabilitiesExecutor function already invoked with non-undefined values.")
 MSG_DEF(JSMSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE,    0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the resolve function.")
 MSG_DEF(JSMSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE,     0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the reject function.")
 MSG_DEF(JSMSG_PROMISE_ERROR_IN_WRAPPED_REJECTION_REASON,0, JSEXN_INTERNALERR, "Promise rejection value is a non-unwrappable cross-compartment wrapper.")
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4871,26 +4871,26 @@ JS::GetModuleHostDefinedField(JSObject* 
 }
 
 JS_PUBLIC_API(bool)
 JS::ModuleDeclarationInstantiation(JSContext* cx, JS::HandleObject moduleArg)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, moduleArg);
-    return ModuleObject::DeclarationInstantiation(cx, moduleArg.as<ModuleObject>());
+    return ModuleObject::Instantiate(cx, moduleArg.as<ModuleObject>());
 }
 
 JS_PUBLIC_API(bool)
 JS::ModuleEvaluation(JSContext* cx, JS::HandleObject moduleArg)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, moduleArg);
-    return ModuleObject::Evaluation(cx, moduleArg.as<ModuleObject>());
+    return ModuleObject::Evaluate(cx, moduleArg.as<ModuleObject>());
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::GetRequestedModules(JSContext* cx, JS::HandleObject moduleArg)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, moduleArg);
--- a/js/src/shell/ModuleLoader.js
+++ b/js/src/shell/ModuleLoader.js
@@ -1,8 +1,9 @@
+/* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* global getModuleLoadPath setModuleResolveHook parseModule os */
 
 // A basic synchronous module loader for testing the shell.
 {
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -233,18 +233,18 @@
     macro(minimumIntegerDigits, minimumIntegerDigits, "minimumIntegerDigits") \
     macro(minimumSignificantDigits, minimumSignificantDigits, "minimumSignificantDigits") \
     macro(minusSign, minusSign, "minusSign") \
     macro(minute, minute, "minute") \
     macro(missingArguments, missingArguments, "missingArguments") \
     macro(mode, mode, "mode") \
     macro(module, module, "module") \
     macro(Module, Module, "Module") \
-    macro(ModuleDeclarationInstantiation, ModuleDeclarationInstantiation, "ModuleDeclarationInstantiation") \
-    macro(ModuleEvaluation, ModuleEvaluation, "ModuleEvaluation") \
+    macro(ModuleInstantiate, ModuleInstantiate, "ModuleInstantiate") \
+    macro(ModuleEvaluate, ModuleEvaluate, "ModuleEvaluate") \
     macro(month, month, "month") \
     macro(multiline, multiline, "multiline") \
     macro(name, name, "name") \
     macro(nan, nan, "nan") \
     macro(NaN, NaN, "NaN") \
     macro(NegativeInfinity, NegativeInfinity, "-Infinity") \
     macro(new, new_, "new") \
     macro(next, next, "next") \
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1977,34 +1977,22 @@ intrinsic_InstantiateModuleFunctionDecla
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
     RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
     args.rval().setUndefined();
     return ModuleObject::instantiateFunctionDeclarations(cx, module);
 }
 
 static bool
-intrinsic_SetModuleState(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 2);
-    RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
-    ModuleState newState = args[1].toInt32();
-    module->setState(newState);
-    args.rval().setUndefined();
-    return true;
-}
-
-static bool
-intrinsic_EvaluateModule(JSContext* cx, unsigned argc, Value* vp)
+intrinsic_ExecuteModule(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
     RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
-    return ModuleObject::evaluate(cx, module, args.rval());
+    return ModuleObject::execute(cx, module, args.rval());
 }
 
 static bool
 intrinsic_NewModuleNamespace(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
     RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
@@ -2561,18 +2549,17 @@ static const JSFunctionSpec intrinsic_fu
           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",
           intrinsic_InstantiateModuleFunctionDeclarations, 1, 0),
-    JS_FN("SetModuleState", intrinsic_SetModuleState, 1, 0),
-    JS_FN("EvaluateModule", intrinsic_EvaluateModule, 1, 0),
+    JS_FN("ExecuteModule", intrinsic_ExecuteModule, 1, 0),
     JS_FN("NewModuleNamespace", intrinsic_NewModuleNamespace, 2, 0),
     JS_FN("AddModuleNamespaceBinding", intrinsic_AddModuleNamespaceBinding, 4, 0),
     JS_FN("ModuleNamespaceExports", intrinsic_ModuleNamespaceExports, 1, 0),
 
     JS_FN("CreatePendingPromise", intrinsic_CreatePendingPromise, 0, 0),
     JS_FN("CreatePromiseResolvedWith", intrinsic_CreatePromiseResolvedWith, 1, 0),
     JS_FN("CreatePromiseRejectedWith", intrinsic_CreatePromiseRejectedWith, 1, 0),
     JS_FN("ResolvePromise", intrinsic_ResolvePromise, 2, 0),