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

Use new host hooks for importing modules #124

Merged
merged 1 commit into from
Jan 23, 2023
Merged
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
121 changes: 49 additions & 72 deletions spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ <h1>EvaluateImportCall ( _specifierExpression_ [ , _optionsExpression_ ] )</h1>
1. Append { [[Key]]: _key_, [[Value]]: _value_ } to _assertions_.
1. Sort _assertions_ by the code point order of the [[Key]] of each element. NOTE: This sorting is observable only in that hosts are prohibited from distinguishing among assertions by the order they occur in.
1. Let _moduleRequest_ be a new ModuleRequest Record { [[Specifier]]: _specifierString_, [[Assertions]]: _assertions_ }.
1. Perform ! HostImportModuleDynamically(_referencingScriptOrModule_, _moduleRequest_, _promiseCapability_).
1. Perform HostLoadImportedModule(_referrer_, _moduleRequest_, ~empty~, _promiseCapability_).
1. Return _promiseCapability_.[[Promise]].
</emu-alg>
</emu-clause>
Expand All @@ -120,39 +120,42 @@ <h1>EvaluateImportCall ( _specifierExpression_ [ , _optionsExpression_ ] )</h1>
</emu-clause>
</emu-clause>

<emu-clause id="sec-hostresolveimportedmodule" aoid="HostResolveImportedModule">
<h1>Runtime Semantics: HostResolveImportedModule ( _referencingScriptOrModule_, <del>_specifier_</del> <ins>_moduleRequest_</ins> )</h1>
<p>HostResolveImportedModule is an implementation-defined abstract operation that provides the concrete Module Record subclass instance that corresponds to <del>the |ModuleSpecifier| String, _specifier_,</del><ins>the ModuleRequest Record _moduleRequest_</del> occurring within the context of the script or module represented by the Script Record or Module Record _referencingScriptOrModule_. _referencingScriptOrModule_ may also be *null*, if the resolution is being performed in the context of an <emu-xref href="#sec-import-calls">`import()`</emu-xref> expression, and there is no active script or module at that time.</ins></p>
<emu-clause id="sec-hostloadimportedmodule" aoid="HostLoadImportedModule">
<h1>HostLoadImportedModule ( _referrer_, <del>_specifier_</del><ins>_moduleRequest_</ins>, _hostDefined_, _payload_ )</h1>
<p>The host-defined abstract operation HostLoadImportedModule takes arguments _referrer_ (a Script Record, a Cyclic Module Record, or a Realm Record), <del>_specifier_ (a String)</del><ins>_moduleRequest_ (a ModuleRequest Record)</ins>, _hostDefined_ (anything), and _payload_ (a GraphLoadingState Record or a PromiseCapability Record) and returns ~unused~.</p>

<emu-note>
<p>An example of when _referencingScriptOrModule_ can be *null* is in a web browser host. There, if a user clicks on a control given by</p>
<p>An example of when _referrer_ can be a Realm Record is in a web browser host. There, if a user clicks on a control given by</p>

<pre><code class="html">&lt;button type="button" onclick="import('./foo.mjs')"&gt;Click me&lt;/button&gt;</code></pre>

<p>there will be no active script or module at the time the <emu-xref href="#sec-import-calls">`import()`</emu-xref> expression runs. More generally, this can happen in any situation where the host pushes execution contexts with *null* ScriptOrModule components onto the execution context stack.</p>
</emu-note>

<p>The implementation of HostResolveImportedModule must conform to the following requirements:</p>
<p>An implementation of HostLoadImportedModule must conform to the following requirements:</p>
<ul>
<li>
The normal return value must be an instance of a concrete subclass of Module Record.
The host environment must perform FinishLoadingImportedModule(_referrer_, <del>_specifier_</del><ins>_moduleRequest_</ins>, _payload_, _result_), where _result_ is either a normal completion containing the loaded Module Record or a throw completion, either synchronously or asynchronously.
</li>
<li>
If a Module Record corresponding to the pair _referencingScriptOrModule_, <del>_specifier_</del>, <ins>_moduleRequest_</ins> does not exist or cannot be created, an exception must be thrown.
</li>
<li>
Each time this operation is called with a specific _referencingScriptOrModule_, <del>_specifier_ pair</del> <ins>_moduleRequest_.[[Specifier]], _moduleRequest_.[[Assertions]] triple</ins> as arguments it must return the same Module Record instance if it completes normally.
<p>If this operation is called multiple times with the same <del>(_referrer_, _specifier_) pair</del><ins>(_referrer_, _moduleRequest_.[[Specifier]], _moduleRequest_.[[Assertions]]) triple</ins> and it performs FinishLoadingImportedModule(_referrer_, <del>_specifier_</del><ins>_moduleRequest_</ins>, _payload_, _result_) where _result_ is a normal completion, then it must perform FinishLoadingImportedModule(_referrer_, <del>_specifier_</del><ins>_moduleRequest_</ins>, _payload_, _result_) with the same _result_ each time.</p>

<p><ins>It is recommended but not required that implementations additionally conform to the following stronger constraint:</ins></p>
<ul>
<li>
It is recommended but not required that implementations additionally conform to the following stronger constraint: each time this operation is called with a specific _referencingScriptOrModule_, _moduleRequest_.[[Specifier]] pair as arguments it must return the same Module Record instance if it completes normally.
<ins>If this operation is called multiple times with the same (_referrer_, _moduleRequest_.[[Specifier]]) pair and it performs FinishLoadingImportedModule(_referrer_, _moduleRequest_, _payload_, _result_) where _result_ is a normal completion, then it must perform FinishLoadingImportedModule(_referrer_, _moduleRequest_, _payload_, _result_) with the same _result_ each time.</ins>
</li>
</ul>
</li>
<li>
<ins>_moduleRequest_.[[Assertions]] must not influence the interpretation of the module or the module specifier; instead, it may be used to determine whether the algorithm completes normally or with an abrupt completion.</ins>
</li>
<li>
The operation must treat _payload_ as an opaque value to be passed through to FinishLoadingImportedModule.
</li>
</ul>
<p>Multiple different _referencingScriptOrModule_, <del>_specifier_</del> <ins>_moduleRequest_.[[Specifier]]</ins> pairs may map to the same Module Record instance. The actual mapping semantic is implementation-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>

<p>The actual process performed is host-defined, but typically consists of performing whatever I/O operations are necessary to load the appropriate Module Record. Multiple different (_referrer_, <del>_specifier_</del><ins>_moduleRequest_.[[Specifier]]</ins>) pairs may map to the same Module Record instance. The actual mapping semantic is host-defined but typically a normalization process is applied to <del>_specifier_</del><ins>_moduleRequest_.[[Specifier]]</ins> as part of the mapping process. A typical normalization process would include actions such as expansion of relative and abbreviated path specifiers.</p>

<emu-note type=editor>
<p>The above text implies that is recommended but not required that hosts do not use _moduleRequest_.[[Assertions]] as part of the module cache key. In either case, an exception thrown from an import with a given assertion list does not rule out success of another import with the same specifier but a different assertion list.</p>
Expand All @@ -166,67 +169,41 @@ <h1>Runtime Semantics: HostResolveImportedModule ( _referencingScriptOrModule_,
</emu-note>
</emu-clause>

<emu-clause id="sec-hostimportmoduledynamically" aoid="HostImportModuleDynamically">
<h1>Runtime Semantics: HostImportModuleDynamically ( _referencingScriptOrModule_, <del>_specifier_,<del> <ins>_moduleRequest_,</ins> _promiseCapability_ )</h1>
<p>HostImportModuleDynamically is an implementation-defined abstract operation that performs any necessary setup work in order to make available the module corresponding to the <del>|ModuleSpecifier| String, _specifier_,</del><ins>ModuleRequest Record _moduleRequest_</ins> occurring within the context of the script or module represented by the Script Record or Module Record _referencingScriptOrModule_. (_referencingScriptOrModule_ may also 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.</p>


<p>The implementation of HostImportModuleDynamically must conform to the following requirements:</p>

<ul>
<li>
The abstract operation must always complete normally with *undefined*. Success or failure must instead be signaled as discussed below.
</li>
<li>
The host environment must conform to one of the two following sets of requirements:
<dl>
<dt>Success path</dt>

<dd>
<ul>
<li>At some future time, the host environment must perform FinishDynamicImport(_referencingScriptOrModule_, <del>_specifier_,</del> <ins>_moduleRequest_</ins>, _promiseCapability_, NormalCompletion(*undefined*)).</li>

<li>Any subsequent call to HostResolveImportedModule after FinishDynamicImport has completed, given the arguments _referencingScriptOrModule_, and <del>_specifier_</del> <ins>_moduleRequest_</ins> must complete normally.</li>

<li>The completion value of any subsequent call to HostResolveImportedModule after FinishDynamicImport has completed, given the arguments _referencingScriptOrModule_, and <del>_specifier_,</del> <ins>_moduleRequest_</ins> must be 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_, <del>_specifier_,</del> <ins>_moduleRequest_,</ins> _promiseCapability_, an abrupt completion), with the abrupt completion representing the cause of failure.</li>
</ul>
</dd>
</dl>
</li>
<li>
If the host environment takes the success path once for a given _referencingScriptOrModule_, <del>_specifier_,</del> <ins>_moduleRequest_</ins> 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 implementation-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-finishdynamicimport" aoid="FinishDynamicImport">
<h1>Runtime Semantics: FinishDynamicImport ( _referencingScriptOrModule_, <del>_specifier_,</del> <ins>_moduleRequest_,</ins> _promiseCapability_, _completion_ )</h1>
<p>The abstract operation FinishDynamicImport takes arguments _referencingScriptOrModule_, <del>_specifier_,</del> <ins>_moduleRequest_ (a ModuleRequest Record),</ins> _promiseCapability_, and _completion_. FinishDynamicImport completes the process of a dynamic import originally started by an <emu-xref href="#sec-import-calls">`import()`</emu-xref> call, resolving or rejecting the promise returned by that call as appropriate according to _completion_. It is performed by host environments as part of HostImportModuleDynamically. It performs the following steps when called:</p>

<emu-clause id="sec-FinishLoadingImportedModule" type="abstract operation">
<h1>FinishLoadingImportedModule ( _referrer_, <del>_specifier_</del><ins>_moduleRequest_</ins>, _payload_, _result_ )</h1>
<p>The abstract operation FinishLoadingImportedModule takes arguments _referrer_ (a Script Record, a Cyclic Module Record, or a Realm Record), <del>_specifier_ (a String)</del><ins>_moduleRequest_ (a ModuleRequest Record)</ins>, _payload_ (a GraphLoadingState Record or a PromiseCapability Record), and _result_ (either a normal completion containing a Module Record or a throw completion) and returns ~unused~.</p>

<emu-alg>
1. If _completion_ is an abrupt completion, then perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « _completion_.[[Value]] »).
1. If _result_ is a normal completion, then
1. If _referrer_.[[LoadedModules]] contains a Record _record_ such that _record_.[[Specifier]] is _moduleRequest_.[[Specifier]] <ins>and AssertionsEqual(_record_.[[Assertions]], _moduleRequest_.[[Assertions]]) is *true*</ins>, then
1. Assert: _record_.[[Module]] is _result_.[[Value]].
1. Else, add Record { [[Specifier]]: _moduleRequest_.[[Specifier]], <ins>[[Assertions]]: _moduleRequest_.[[Assertions]]</ins>, [[Module]]: _result_.[[Value]] } to _referrer_.[[LoadedModules]].
1. If _payload_ is a GraphLoadingState Record, then
1. Perform ContinueModuleLoading(_payload_, _result_).
1. Else,
1. Assert: _completion_ is a normal completion and _completion_.[[Value]] is *undefined*.
1. Let _moduleRecord_ be ! HostResolveImportedModule(_referencingScriptOrModule_, <del>_specifier_,</del> <ins>_moduleRequest_</ins>).
1. Assert: Evaluate has already been invoked on _moduleRecord_ and successfully completed.
1. Let _namespace_ be GetModuleNamespace(_moduleRecord_).
1. If _namespace_ is an abrupt completion, perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « _namespace_.[[Value]] »).
1. Else, perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « _namespace_.[[Value]] »).
1. Perform ContinueDynamicImport(_payload_, _result_).
1. Return ~unused~.
</emu-alg>

<emu-note type="editor">
<p>The description of the [[LoadedModules]] field of Realm Record, Script Record, and Cyclic Module Record should be updated to include the [[Assertions]] field.</p>
</emu-note>

<emu-clause id="sec-FinishLoadingImportedModule-AssertionsEqual" type="abstract operation">
<h1><ins>AssertionsEqual(_left_, _right_)</ins></h1>
<p>The abstract operation AssertionsEqual takes arguments _left_ and _right_ (two Lists of Records { [[Key]]: a String, [[Value]]: a String }), and returns a Boolean.</p>

<emu-alg>
1. If the number of elements in _left_ is not the same as the number of elements in _right_, return *false*.
1. For each Record { [[Key]], [[Value]] } _r_ of _left_, do
1. Let _found_ be *false*.
1. For each Record { [[Key]], [[Value]] } _s_ of _right_, do
1. If _r_.[[Key]] is _s_.[[Key]] and _r_.[[Value]] is _s_.[[Value]], then
1. Set _found_ to *true*.
1. If _found_ is *false*, return *false*.
1. Return *true*.
</emu-alg>
</emu-clause>
</emu-clause>

<emu-clause id="sec-hostgetsupportedimportassertions" aoid="HostGetSupportedImportAssertions">
Expand Down Expand Up @@ -312,7 +289,7 @@ <h1>Static Semantics: StringValue</h1>
<h1>ModuleRequest Records</h1>

<p>A <dfn>ModuleRequest Record</dfn> represents the request to import a module with given import assertions. It consists of the following fields:</p>
<emu-table id="table-cyclic-module-fields" caption="ModuleRequest Record fields">
<emu-table id="table-modulerequest-fields" caption="ModuleRequest Record fields">
<table>
<tbody>
<tr>
Expand Down Expand Up @@ -558,7 +535,7 @@ <h1>Sample host integration: The Web embedding</h1>

<ul>
<li>The <a href="https://html.spec.whatwg.org/#module-script">module script</a> would have an additional item, which would be the module type, as a string (e.g., *"json"*), or *undefined* for a JavaScript module.</li>
<li>HostResolveImportedModule and HostImportModuleDynamically would take a ModuleRequest Record parameter in place of a specifier string, which would be passed down through several abstract operations to reach the <a href="https://html.spec.whatwg.org/#fetch-a-single-module-script">fetch a single module script</a> algorithm. Somewhere near the entrypoint, if the ModuleRequest Record's [[Assertions]] field has an element _entry_ such that _entry_.[[Key]] is *"type"*, then let _type_ be _entry_.[[Value]]; otherwise let _type_ be *undefined*. If the type is invalid, then an exception is thrown and module loading fails. Otherwise, this will equal the module type, if the module can be successfully fetched with a matching MIME type.</li>
<li>HostLoadImportedModule would take a ModuleRequest Record parameter in place of a specifier string, which would be passed down through several abstract operations to reach the <a href="https://html.spec.whatwg.org/#fetch-a-single-module-script">fetch a single module script</a> algorithm. Somewhere near the entrypoint, if the ModuleRequest Record's [[Assertions]] field has an element _entry_ such that _entry_.[[Key]] is *"type"*, then let _type_ be _entry_.[[Value]]; otherwise let _type_ be *undefined*. If the type is invalid, then an exception is thrown and module loading fails. Otherwise, this will equal the module type, if the module can be successfully fetched with a matching MIME type.</li>
<li>In the <a href="https://html.spec.whatwg.org/#fetch-the-descendants-of-a-module-script">fetch the descendents of a module script</a> algorithm, when iterating over [[RequestedModules]], the elements are ModuleRequest Records rather than just specifier strings; these Records is passed on to the internal module script graph fetching procedure (which sends it to "fetch a single module script". Other usage sites of [[RequestedModules]] ignore the assertion.</li>
<li>"Fetch a single module script" would check the assertion in two places:
<ul>
Expand Down