Skip to content

Commit

Permalink
Store and rethrow module instantiation/evaluation errors
Browse files Browse the repository at this point in the history
This addresses tc39#862 by ensuring that repeated calls to ModuleDeclarationInstantiation() and ModuleEvaluation(), for Source Text Module Records, rethrow any exceptions they previously threw, instead of silently succeeding. This allows host environments to do top-down instantiation/evaluation of module graphs, instead of having to do bottom-up instantiation/evaluation in order to record individual failures and thus prevent future instantiation/evaluation.

In the process, this helps formalize some of the invariants previously stated in a vague way, such as "ModuleDeclarationInstantiation must have completed successfully", replacing them instead with an explicit [[Status]] field whose contents can be asserted against.

For background on the trouble caused by the previous approach of silent success, see:

- whatwg/html#1545
- tc39#862
- whatwg/html#2629
  • Loading branch information
domenic committed May 13, 2017
1 parent 0719f44 commit 99447ee
Showing 1 changed file with 95 additions and 55 deletions.
150 changes: 95 additions & 55 deletions spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -20989,13 +20989,24 @@ <h1>Abstract Module Records</h1>
</tr>
<tr>
<td>
[[Evaluated]]
[[Status]]
</td>
<td>
Boolean
String
</td>
<td>
Initially `"uninstantiated"`. Can become `"instantiated"`, `"instantiating"`, `"evaluated"`, or `"errored"` as the module progresses throughout its lifecycle.
</td>
</tr>
<tr>
<td>
[[ErrorCompletion]]
</td>
<td>
An abrupt completion
</td>
<td>
Initially *false*, *true* if evaluation of this module has started. Remains *true* when evaluation completes, even if it is an abrupt completion.
A completion record representing exception that occurred during instantiation or evaluation; meaningful only if [[Status]] is `"errored"`.
</td>
</tr>
<tr>
Expand Down Expand Up @@ -21044,16 +21055,16 @@ <h1>Abstract Module Records</h1>
ModuleDeclarationInstantiation()
</td>
<td>
Transitively resolve all module dependencies and create a module Environment Record for the module.
Transitively resolve all module dependencies and create a module Environment Record for the module. Will transition this module's [[Status]] from `"uninstantiated"` to either `"instantiated"` or `"errored"`. While this is executing, this module's [[Status]] will be `"instantiating"`; this is observable through reentrant invocations via circular dependencies.
</td>
</tr>
<tr>
<td>
ModuleEvaluation()
</td>
<td>
<p>Do nothing if this module has already been evaluated. Otherwise, transitively evaluate all module dependences of this module and then evaluate this module.</p>
<p>ModuleDeclarationInstantiation must be completed prior to invoking this method.</p>
<p>Transitively evaluate all module dependences of this module and then evaluate this module.</p>
<p>Will transition this module's [[Status]] from `"instantiated"` to either `"evaluated"` or `"errored"`; if the status is already in one of those states, do nothing or return [[ErrorCompletion]] as appropriate. This method will never be invoked when the module's status is `"uninstantiated"`.</p>
</td>
</tr>
</tbody>
Expand Down Expand Up @@ -21646,52 +21657,73 @@ <h1>ModuleDeclarationInstantiation( ) Concrete Method</h1>
<p>The ModuleDeclarationInstantiation concrete method of a Source Text Module Record performs the following steps:</p>
<emu-alg>
1. Let _module_ be this Source Text Module Record.
1. Let _realm_ be _module_.[[Realm]].
1. Assert: _realm_ is not *undefined*.
1. Let _code_ be _module_.[[ECMAScriptCode]].
1. If _module_.[[Environment]] is not *undefined*, return NormalCompletion(~empty~).
1. Let _env_ be NewModuleEnvironment(_realm_.[[GlobalEnv]]).
1. Set _module_.[[Environment]] to _env_.
1. For each String _required_ that is an element of _module_.[[RequestedModules]], do
1. NOTE: Before instantiating a module, all of the modules it requested must be available. An implementation may perform this test at any time prior to this point.
1. Let _requiredModule_ be ? HostResolveImportedModule(_module_, _required_).
1. Perform ? _requiredModule_.ModuleDeclarationInstantiation().
1. For each ExportEntry Record _e_ in _module_.[[IndirectExportEntries]], do
1. Let _resolution_ be ? _module_.ResolveExport(_e_.[[ExportName]], &laquo; &raquo;).
1. If _resolution_ is *null* or _resolution_ is `"ambiguous"`, throw a *SyntaxError* exception.
1. Assert: All named exports from _module_ are resolvable.
1. Let _envRec_ be _env_'s EnvironmentRecord.
1. For each ImportEntry Record _in_ in _module_.[[ImportEntries]], do
1. Let _importedModule_ be ! HostResolveImportedModule(_module_, _in_.[[ModuleRequest]]).
1. NOTE: The above call cannot fail because imported module requests are a subset of _module_.[[RequestedModules]], and these have been resolved earlier in this algorithm.
1. If _in_.[[ImportName]] is `"*"`, then
1. Let _namespace_ be ? GetModuleNamespace(_importedModule_).
1. Perform ! _envRec_.CreateImmutableBinding(_in_.[[LocalName]], *true*).
1. Call _envRec_.InitializeBinding(_in_.[[LocalName]], _namespace_).
1. Else,
1. Let _resolution_ be ? _importedModule_.ResolveExport(_in_.[[ImportName]], &laquo; &raquo;).
1. Let _result_ be InnerModuleDeclarationInstantiation(_module_).
1. If _result_ is an abrupt completion,
1. Set _module_.[[Status]] to `"errored"`.
1. Set _module_.[[ErrorCompletion]] to _result_.
1. Otherwise, set _module_.[[Status]] to `"instantiated"`.
1. Return _result_.
</emu-alg>

<emu-clause id="sec-innermoduledeclarationinstantiation" aoid="InnerModuleDeclarationInstantiation">
<h1>Runtime Semantics: InnerModuleDeclarationInstantiation( _module_ )</h1>
<p>The InnerModuleDeclarationInstantiation abstract operation performs the following steps:</p>
<emu-alg>
1. Let _realm_ be _module_.[[Realm]].
1. Assert: _realm_ is not *undefined*.
1. Let _code_ be _module_.[[ECMAScriptCode]].
1. If _module_.[[Status]] is `"errored"`, return _module_.[[ErrorCompletion]].
1. If _module_.[[Status]] is `"instantiating"`, `"instantiated"` or `"evaluated"`,
1. Assert: _module_.[[Environment]] is not *undefined*.
1. Return NormalCompletion(~empty~).
1. Assert: _module_.[[Status]] is `"uninstantiated"`.
1. Let _env_ be NewModuleEnvironment(_realm_.[[GlobalEnv]]).
1. Set _module_.[[Environment]] to _env_.
1. Set _module_.[[Status]] to `"instantiating"`.
1. For each String _required_ that is an element of _module_.[[RequestedModules]],
1. NOTE: Before instantiating a module, all of the modules it requested must be available. An implementation may perform this test at any time prior to this point.
1. Perform ? HostResolveImportedModule(_module_, _required_).
1. For each String _required_ that is an element of _module_.[[RequestedModules]],
1. NOTE: this loop is separate from the previous one to ensure that exceptions due to unresolvable modules are thrown before attempting to instantiate any modules.
1. Let _requiredModule_ be ! HostResolveImportedModule(_module_, _required_).
1. Perform ? _requiredModule_.ModuleDeclarationInstantiation().
1. For each ExportEntry Record _e_ in _module_.[[IndirectExportEntries]], do
1. Let _resolution_ be ? _module_.ResolveExport(_e_.[[ExportName]], &laquo; &raquo;).
1. If _resolution_ is *null* or _resolution_ is `"ambiguous"`, throw a *SyntaxError* exception.
1. Call _envRec_.CreateImportBinding(_in_.[[LocalName]], _resolution_.[[Module]], _resolution_.[[BindingName]]).
1. Let _varDeclarations_ be the VarScopedDeclarations of _code_.
1. Let _declaredVarNames_ be a new empty List.
1. For each element _d_ in _varDeclarations_, do
1. For each element _dn_ of the BoundNames of _d_, do
1. If _dn_ is not an element of _declaredVarNames_, then
1. Perform ! _envRec_.CreateMutableBinding(_dn_, *false*).
1. Call _envRec_.InitializeBinding(_dn_, *undefined*).
1. Append _dn_ to _declaredVarNames_.
1. Let _lexDeclarations_ be the LexicallyScopedDeclarations of _code_.
1. For each element _d_ in _lexDeclarations_, do
1. For each element _dn_ of the BoundNames of _d_, do
1. If IsConstantDeclaration of _d_ is *true*, then
1. Perform ! _envRec_.CreateImmutableBinding(_dn_, *true*).
1. Assert: All named exports from _module_ are resolvable.
1. Let _envRec_ be _env_'s EnvironmentRecord.
1. For each ImportEntry Record _in_ in _module_.[[ImportEntries]], do
1. Let _importedModule_ be ! HostResolveImportedModule(_module_, _in_.[[ModuleRequest]]).
1. NOTE: The above call cannot fail because imported module requests are a subset of _module_.[[RequestedModules]], and these have been resolved earlier in this algorithm.
1. If _in_.[[ImportName]] is `"*"`, then
1. Let _namespace_ be ? GetModuleNamespace(_importedModule_).
1. Perform ! _envRec_.CreateImmutableBinding(_in_.[[LocalName]], *true*).
1. Call _envRec_.InitializeBinding(_in_.[[LocalName]], _namespace_).
1. Else,
1. Perform ! _envRec_.CreateMutableBinding(_dn_, *false*).
1. If _d_ is a |FunctionDeclaration|, a |GeneratorDeclaration|, or an |AsyncFunctionDeclaration|, then
1. Let _fo_ be the result of performing InstantiateFunctionObject for _d_ with argument _env_.
1. Call _envRec_.InitializeBinding(_dn_, _fo_).
1. Return NormalCompletion(~empty~).
</emu-alg>
1. Let _resolution_ be ? _importedModule_.ResolveExport(_in_.[[ImportName]], &laquo; &raquo;).
1. If _resolution_ is *null* or _resolution_ is `"ambiguous"`, throw a *SyntaxError* exception.
1. Call _envRec_.CreateImportBinding(_in_.[[LocalName]], _resolution_.[[Module]], _resolution_.[[BindingName]]).
1. Let _varDeclarations_ be the VarScopedDeclarations of _code_.
1. Let _declaredVarNames_ be a new empty List.
1. For each element _d_ in _varDeclarations_, do
1. For each element _dn_ of the BoundNames of _d_, do
1. If _dn_ is not an element of _declaredVarNames_, then
1. Perform ! _envRec_.CreateMutableBinding(_dn_, *false*).
1. Call _envRec_.InitializeBinding(_dn_, *undefined*).
1. Append _dn_ to _declaredVarNames_.
1. Let _lexDeclarations_ be the LexicallyScopedDeclarations of _code_.
1. For each element _d_ in _lexDeclarations_, do
1. For each element _dn_ of the BoundNames of _d_, do
1. If IsConstantDeclaration of _d_ is *true*, then
1. Perform ! _envRec_.CreateImmutableBinding(_dn_, *true*).
1. Else,
1. Perform ! _envRec_.CreateMutableBinding(_dn_, *false*).
1. If _d_ is a |FunctionDeclaration|, a |GeneratorDeclaration|, or an |AsyncFunctionDeclaration|, then
1. Let _fo_ be the result of performing InstantiateFunctionObject for _d_ with argument _env_.
1. Call _envRec_.InitializeBinding(_dn_, _fo_).
1. Return NormalCompletion(~empty~).
</emu-alg>
</emu-clause>
</emu-clause>

<!-- es6num="15.2.1.16.5" -->
Expand All @@ -21700,14 +21732,18 @@ <h1>ModuleEvaluation( ) Concrete Method</h1>
<p>The ModuleEvaluation concrete method of a Source Text Module Record performs the following steps:</p>
<emu-alg>
1. Let _module_ be this Source Text Module Record.
1. Assert: ModuleDeclarationInstantiation has already been invoked on _module_ and successfully completed.
1. Assert: _module_.[[Realm]] is not *undefined*.
1. If _module_.[[Evaluated]] is *true*, return *undefined*.
1. Set _module_.[[Evaluated]] to *true*.
1. Assert: _module_.[[Status]] is not `"uninstantiated"` or `"instantiating"`.
1. If _module_.[[Status]] is `"evaluated"`, return *undefined*.
1. If _module_.[[Status]] is `"errored"`, return _module_.[[ErrorCompletion]].
1. For each String _required_ that is an element of _module_.[[RequestedModules]], do
1. Let _requiredModule_ be ! HostResolveImportedModule(_module_, _required_).
1. NOTE: ModuleDeclarationInstantiation must be completed prior to invoking this method, so every requested module is guaranteed to resolve successfully.
1. Perform ? _requiredModule_.ModuleEvaluation().
1. NOTE: ModuleDeclarationInstantiation must be completed successfully prior to invoking this method, so every requested module is guaranteed to resolve successfully.
1. Let _requiredModuleStatus_ be _requiredModule_.ModuleEvaluation().
1. If _requiredModuleStatus_ is an abrupt completion,
1. Set _module_.[[Status]] to `"errored"`.
1. Set _module_.[[ErrorCompletion]] to _requiredModuleStatus_.
1. Return _requiredModuleStatus_.
1. Let _moduleCxt_ be a new ECMAScript code execution context.
1. Set the Function of _moduleCxt_ to *null*.
1. Set the Realm of _moduleCxt_ to _module_.[[Realm]].
Expand All @@ -21720,6 +21756,10 @@ <h1>ModuleEvaluation( ) Concrete Method</h1>
1. Let _result_ be the result of evaluating _module_.[[ECMAScriptCode]].
1. Suspend _moduleCxt_ and remove it from the execution context stack.
1. Resume the context that is now on the top of the execution context stack as the running execution context.
1. If _result_ is an abrupt completion,
1. Set _module_.[[Status]] to `"errored"`.
1. Set _module_.[[ErrorCompletion]] to _result_.
1. Otherwise, set _module_.[[Status]] to `"evaluated"`.
1. Return Completion(_result_).
</emu-alg>
</emu-clause>
Expand Down

0 comments on commit 99447ee

Please sign in to comment.