Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specify how importing a module block works #2

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 133 additions & 24 deletions spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -11660,6 +11660,34 @@ <h1>Realms</h1>
<emu-note>Once a Parse Node becomes unreachable, the corresponding [[Array]] is also unreachable, and it would be unobservable if an implementation removed the pair from the [[TemplateMap]] list.</emu-note>
</td>
</tr>
<tr>
<td>
[[ModuleMap]]
</td>
<td>
a List of Record { [[ReferencingScriptOrModule]]: Script Record or Module Record or *null*, [[Specifier]]: String or Object, [[ModuleRecord]]: Module Record }
</td>
<td>
<p>Module Records are cached separately per Realm: two imports from the same realm using the same specifier from the same referencing script or module must resolve to the same Module Record. The cache needs to be per-Realm to support cases when [[ReferencingScriptOrModule]] is *null*.</p>
<p>This List respects the following invariants:</p>
<ul>
<li>
It does not contain two Records _record1_ and _record2_ such that _record1_.[[ReferencingScriptOrModule]] is _record2_.[[ReferencingScriptOrModule]] and _record1_.[[Specifier]] is _record2_.[[Specifier]].
</li>
<li>
<p>For every Record _record_ it contains, if Type(_record_.[[Specifier]]) is Object then:</p>
<ul>
<li>
_record_.[[Specifier]] has a [[ModuleBlockBody]] internal slot.
</li>
<li>
_record_.[[ReferencingScriptOrModule]] is *null*.
</li>
</ul>
</li>
</ul>
</td>
</tr>
<tr>
<td>
[[HostDefined]]
Expand Down Expand Up @@ -19372,7 +19400,7 @@ <h1>Runtime Semantics: Evaluation</h1>
1. Let _promiseCapability_ be ! NewPromiseCapability(%Promise%).
1. Let _specifierString_ be Completion(ToString(_specifier_)).
1. IfAbruptRejectPromise(_specifierString_, _promiseCapability_).
1. Perform HostImportModuleDynamically(_referencingScriptOrModule_, _specifierString_, _promiseCapability_).
1. Perform ImportModuleDynamically(_referencingScriptOrModule_, _specifierString_, _promiseCapability_).
1. Return _promiseCapability_.[[Promise]].
</emu-alg>
</emu-clause>
Expand Down Expand Up @@ -26379,7 +26407,7 @@ <h1>
1. Set _index_ to _index_ + 1.
1. Append _module_ to _stack_.
1. For each String _required_ of _module_.[[RequestedModules]], do
1. Let _requiredModule_ be ? HostResolveImportedModule(_module_, _required_).
1. Let _requiredModule_ be ? ResolveImportedModule(_module_, _required_).
1. Set _index_ to ? InnerModuleLinking(_requiredModule_, _stack_, _index_).
1. If _requiredModule_ is a Cyclic Module Record, then
1. Assert: _requiredModule_.[[Status]] is either ~linking~, ~linked~, ~evaluating-async~, or ~evaluated~.
Expand Down Expand Up @@ -26472,7 +26500,7 @@ <h1>
1. Set _index_ to _index_ + 1.
1. Append _module_ to _stack_.
1. For each String _required_ of _module_.[[RequestedModules]], do
1. Let _requiredModule_ be ! HostResolveImportedModule(_module_, _required_).
1. Let _requiredModule_ be ! ResolveImportedModule(_module_, _required_).
1. NOTE: Link must be completed successfully prior to invoking this method, so every requested module is guaranteed to resolve successfully.
1. Set _index_ to ? InnerModuleEvaluation(_requiredModule_, _stack_, _index_).
1. If _requiredModule_ is a Cyclic Module Record, then
Expand Down Expand Up @@ -27598,7 +27626,7 @@ <h1>
1. Assert: _module_ imports a specific binding for this export.
1. Append _e_.[[ExportName]] to _exportedNames_.
1. For each ExportEntry Record _e_ of _module_.[[StarExportEntries]], do
1. Let _requestedModule_ be ? HostResolveImportedModule(_module_, _e_.[[ModuleRequest]]).
1. Let _requestedModule_ be ? ResolveImportedModule(_module_, _e_.[[ModuleRequest]]).
1. Let _starNames_ be ? _requestedModule_.GetExportedNames(_exportStarSet_).
1. For each element _n_ of _starNames_, do
1. If SameValue(_n_, *"default"*) is *false*, then
Expand Down Expand Up @@ -27642,7 +27670,7 @@ <h1>
1. Return ResolvedBinding Record { [[Module]]: _module_, [[BindingName]]: _e_.[[LocalName]] }.
1. For each ExportEntry Record _e_ of _module_.[[IndirectExportEntries]], do
1. If SameValue(_exportName_, _e_.[[ExportName]]) is *true*, then
1. Let _importedModule_ be ? HostResolveImportedModule(_module_, _e_.[[ModuleRequest]]).
1. Let _importedModule_ be ? ResolveImportedModule(_module_, _e_.[[ModuleRequest]]).
1. If _e_.[[ImportName]] is ~all~, then
1. Assert: _module_ does not provide the direct binding for this export.
1. Return ResolvedBinding Record { [[Module]]: _importedModule_, [[BindingName]]: ~namespace~ }.
Expand All @@ -27655,7 +27683,7 @@ <h1>
1. NOTE: A `default` export cannot be provided by an `export * from "mod"` declaration.
1. Let _starResolution_ be *null*.
1. For each ExportEntry Record _e_ of _module_.[[StarExportEntries]], do
1. Let _importedModule_ be ? HostResolveImportedModule(_module_, _e_.[[ModuleRequest]]).
1. Let _importedModule_ be ? ResolveImportedModule(_module_, _e_.[[ModuleRequest]]).
1. Let _resolution_ be ? _importedModule_.ResolveExport(_exportName_, _resolveSet_).
1. If _resolution_ is ~ambiguous~, return ~ambiguous~.
1. If _resolution_ is not *null*, then
Expand Down Expand Up @@ -27688,7 +27716,7 @@ <h1>InitializeEnvironment ( ): either a normal completion containing ~unused~ or
1. Let _env_ be NewModuleEnvironment(_realm_.[[GlobalEnv]]).
1. Set _module_.[[Environment]] to _env_.
1. For each ImportEntry Record _in_ of _module_.[[ImportEntries]], do
1. Let _importedModule_ be ! HostResolveImportedModule(_module_, _in_.[[ModuleRequest]]).
1. Let _importedModule_ be ! ResolveImportedModule(_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 ~namespace-object~, then
1. Let _namespace_ be ? GetModuleNamespace(_importedModule_).
Expand Down Expand Up @@ -27774,6 +27802,27 @@ <h1>
</emu-clause>
</emu-clause>

<emu-clause id="sec-resolveimportedmodule" type="abstract operation">
<h1>
ResolveImportedModule (
_referencingScriptOrModule_: a Script Record, a Module Record, or *null*,
_specifier_: a String or an Object with a [[ModuleBlockBody]] internal slot,
): either a normal completion containing a Module Record or a throw completion
</h1>
<dl class="header"></dl>

<emu-alg>
1. Assert: If Type(_specifier_) is Object, then _referencingScriptOrModule_ is *null*.
1. If there exists a Record _record_ in _currentRealm_.[[ModuleMap]] such that _record_.[[ReferencingScriptOrModule]] is _referencingScriptOrModule_ and _record_.[[Specifier]] is _specifier_, then
1. Return NormalCompletion(_record_.[[ModuleRecord]]).
1. Assert: Type(_specifier_) is String.
1. Let _completion_ be Completion(HostResolveImportedModule(_referencingScriptOrModule_, _specifier_)).
1. If _completion_ is a normal completion, then
1. Add Record { [[ReferencingScriptOrModule]]: _referencingScriptOrModule_, [[Specifier]]: _specifier_, [[ModuleRecord]]: _completion_.[[Value]] } to _currentRealm_.[[ModuleMap]].
1. Return _completion_.
</emu-alg>
</emu-clause>

<emu-clause id="sec-hostresolveimportedmodule" type="host-defined abstract operation">
<h1>
HostResolveImportedModule (
Expand Down Expand Up @@ -27802,25 +27851,56 @@ <h1>
<li>
If a Module Record corresponding to the pair _referencingScriptOrModule_, _specifier_ does not exist or cannot be created, an exception must be thrown.
</li>
<li>
Each time this operation is called with a specific _referencingScriptOrModule_, _specifier_ pair as arguments it must return the same Module Record instance if it completes normally.
</li>
</ul>
<p>Multiple different _referencingScriptOrModule_, _specifier_ pairs may map to the same Module Record instance. The actual mapping semantic is host-defined but typically a normalization process is applied to _specifier_ as part of the mapping process. A typical normalization process would include actions such as alphabetic case folding and expansion of relative and abbreviated path specifiers.</p>
</emu-clause>

<emu-clause id="sec-hostimportmoduledynamically" type="host-defined abstract operation">
<emu-clause id="sec-importmoduledynamically" type="abstract operation">
<h1>
HostImportModuleDynamically (
ImportModuleDynamically (
_referencingScriptOrModule_: a Script Record, a Module Record, or *null*,
_specifier_: a |ModuleSpecifier| String,
_specifier_: a String or an Object with a [[ModuleBlockBody]] internal slot,
_promiseCapability_: a PromiseCapability Record,
): ~unused~
</h1>
<dl class="header">
<dt>description</dt>
<dd>It performs any necessary setup work in order to make available the module corresponding to _specifier_ occurring within the context of the script or module represented by _referencingScriptOrModule_. _referencingScriptOrModule_ may be *null* if there is no active script or module when the <emu-xref href="#sec-import-calls">`import()`</emu-xref> expression occurs. It then performs FinishDynamicImport to finish the dynamic import process.</dd>
</dl>

<emu-alg>
1. If Type(_specifier_) is Object, then
1. Set _referencingScriptOrModule_ to *null*.
1. Let _success_ be a new Abstract Closure that captures _referencingScriptOrModule_, _specifier_, and _promiseCapability_ and performs the following steps when called:
1. Let _promise_ be ! PromiseResolve(%Promise%, *undefined*).
1. Perform FinishDynamicImport(_referencingScriptOrModule_, _specifier_, _promiseCapability_, _promise_).
1. Let _failure_ be a new Abstract Closure with parameters (_error_) that captures _referencingScriptOrModule_, _specifier_, and _promiseCapability_ and performs the following steps when called:
1. Let _pc_ be ! NewPromiseCapability(%Promise%).
1. Perform ! Call(_pc_.[[Reject]], *undefined*, &laquo; _error_ &raquo;).
1. Perform FinishDynamicImport(_referencingScriptOrModule_, _specifier_, _promiseCapability_, _pc_.[[Promise]]).
1. Let _currentRealm_ be the current Realm.
1. If there exists a Record _record_ in _currentRealm_.[[ModuleMap]] such that _record_.[[ReferencingScriptOrModule]] is _referencingScriptOrModule_ and _record_.[[Specifier]] is _specifier_, then
1. Perform _success_().
1. Else if Type(_specifier_) is Object, then
1. Perform ImportModuleBlockDynamically(_specifier_, _success_, _failure_).
1. Else,
1. Perform HostImportModuleDynamically(_referencingScriptOrModule_, _specifier_, _success_, _failure_).
</emu-alg>
</emu-clause>

<emu-clause id="sec-hostimportmoduledynamically" type="host-defined abstract operation">
<h1>
HostImportModuleDynamically (
_referencingScriptOrModule_: a Script Record, a Module Record, or *null*,
_specifier_: a String,
_success_: an Abstract Closure,
_failure_: an Abstract Closure,
): ~unused~
</h1>
<dl class="header">
<dt>description</dt>
<dd>It performs any necessary setup work in order to make available the module corresponding to _specifier_ occurring within the context of the script or module represented by _referencingScriptOrModule_. _referencingScriptOrModule_ may be *null* if there is no active script or module when the <emu-xref href="#sec-import-calls">`import()`</emu-xref> expression occurs.</dd>
</dl>
<p>An implementation of HostImportModuleDynamically must conform to the following requirements:</p>

<ul>
Expand All @@ -27834,32 +27914,61 @@ <h1>

<dd>
<ul>
<li>At some future time, the host environment must perform FinishDynamicImport(_referencingScriptOrModule_, _specifier_, _promiseCapability_, _promise_), where _promise_ is a Promise resolved with *undefined*.</li>

<li>Any subsequent call to HostResolveImportedModule after FinishDynamicImport has completed, given the arguments _referencingScriptOrModule_ and _specifier_, must return a normal completion containing a module which has already been evaluated, i.e. whose Evaluate concrete method has already been called and returned a normal completion.</li>
<li>At some future time, the host environment must perform _success_().</li>
<li>Any subsequent call to HostResolveImportedModule after _success_ has completed, given the arguments _referencingScriptOrModule_ and _specifier_, must return a normal completion containing a module which has already been evaluated, i.e. whose Evaluate concrete method has already been called and returned a normal completion.</li>
</ul>
</dd>

<dt>Failure path</dt>

<dd>
<ul>
<li>At some future time, the host environment must perform FinishDynamicImport(_referencingScriptOrModule_, _specifier_, _promiseCapability_, _promise_), where _promise_ is a Promise rejected with an error representing the cause of failure.</li>
<li>At some future time, the host environment must perform _failure_(_error_), where _error_ is an error representing the cause of failure.</li>
</ul>
</dd>
</dl>
</li>
<li>
If the host environment takes the success path once for a given _referencingScriptOrModule_, _specifier_ pair, it must always do so for subsequent calls.
</li>
<li>
The operation must not call _promiseCapability_.[[Resolve]] or _promiseCapability_.[[Reject]], but instead must treat _promiseCapability_ as an opaque identifying value to be passed through to FinishDynamicImport.
</li>
</ul>

<p>The actual process performed is host-defined, but typically consists of performing whatever I/O operations are necessary to allow HostResolveImportedModule to synchronously retrieve the appropriate Module Record, and then calling its Evaluate concrete method. This might require performing similar normalization as HostResolveImportedModule does.</p>
</emu-clause>

<emu-clause id="sec-importmoduleblockdynamically" type="abstract operation">
<h1>
ImportModuleBlockDynamically (
_moduleBlock_: an Object with a [[ModuleBlockBody]] internal slot,
_success_: an Abstract Closure,
_failure_: an Abstract Closure,
): ~unused~
</h1>
<dl class="header">
<dt>description</dt>
<dd>It links and evaluates the module represented by _moduleBlock_, and if it succeeds it adds the resulting Module Record to the current Realm Record's [[ModuleMap]] List.</dd>
</dl>

<emu-alg>
1. Let _currentRealm_ be the current Realm Record.
1. TODO: ParseModule currently expects a string, not a Parse Node.
1. Let _moduleRecord_ be ParseModule(_moduleBlock_.[[ModuleBlockBody]], _currentRealm_, _moduleBlock_.[[HostDefined]]).
1. Assert: _moduleRecord_ is a Source Text Module Record.
1. Let _linkResult_ be Completion(_moduleRecord_.Link()).
1. If _linkResult_ is an abrupt completion, then
1. Perform _failure_(_linkResult_.[[Value]]).
1. Return ~unused~.
1. Let _evaluationPromise_ be _moduleRecord_.Evaluate().
1. Let _fulfilledClosure_ be a new Abstract Closure that captures _success_, _currentRealm_ and _moduleRecord_ and performs the following steps when called:
1. If there exists a Record _record_ in _currentRealm_.[[ModuleMap]] such that _record_.[[Specifier]] is _specifier_, then
1. Assert: _record_.[[ModuleRecord]] is _moduleRecord_.
1. TODO: Check if this is enough to protect against concurrent calls to ImportModuleBlockDynamically with the same _moduleBlock_.
1. Else,
1. Add Record { [[ReferencingScriptOrModule]]: *null*, [[Specifier]]: _specifier_, [[ModuleRecord]]: _moduleRecord_ } to _currentRealm_.[[ModuleMap]].
1. Perform _success_().
1. Let _onRejected_ be CreateBuiltinFunction(_failure_, 1, *""*, &laquo; &raquo;).
1. Perform PerformPromiseThen(_evaluationPromise_, _onFulfilled_, _onRejected_).
1. Return ~unused~.
</emu-alg>
</emu-clause>

<emu-clause id="sec-finishdynamicimport" type="abstract operation">
<h1>
FinishDynamicImport (
Expand All @@ -27876,7 +27985,7 @@ <h1>
<emu-alg>
1. Let _fulfilledClosure_ be a new Abstract Closure with parameters (_result_) that captures _referencingScriptOrModule_, _specifier_, and _promiseCapability_ and performs the following steps when called:
1. Assert: _result_ is *undefined*.
1. Let _moduleRecord_ be ! HostResolveImportedModule(_referencingScriptOrModule_, _specifier_).
1. Let _moduleRecord_ be ! ResolveImportedModule(_referencingScriptOrModule_, _specifier_).
1. Assert: Evaluate has already been invoked on _moduleRecord_ and successfully completed.
1. Let _namespace_ be Completion(GetModuleNamespace(_moduleRecord_)).
1. If _namespace_ is an abrupt completion, then
Expand Down