-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathspec.emu
295 lines (255 loc) · 12.3 KB
/
spec.emu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
<!doctype html>
<meta charset="utf8">
<link rel="stylesheet" href="./spec.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/styles/github.min.css">
<script src="./spec.js"></script>
<pre class="metadata">
title: Dynamic Import Host Adjustment
stage: 0
contributors: Mike Samuel
</pre>
<p>This proposal allows the host to inspect and adjust the argument,
_x_, in `import(x)` so that Trusted Types can use runtime-type
information to decide whether to allow the import to proceed.</p>
<p>For more detail see the
<a href="https://github.com/tc39/dynamic-import-host-adjustment/blob/master/README.md">explainer</a>.</p>
<p>This proposal lays out two options:</p>
<ul>
<li><a href="#option1">Define a new host callout</a></li>
<li><a href="#option2">Reuse HostImportModuleDynamically</a></li>
</ul>
<h2 id="option1">Option 1: Define a new host callout</h1>
<emu-clause id="sec-import-calls">
<emu-note type="editor">
<p>First, this proposal adjusts `import(...)` to stringify its argument
via a host callout.</p>
</emu-note>
<h1>Import Calls</h1>
<emu-clause id="sec-import-call-runtime-semantics-evaluation">
<h1>Runtime Semantics: Evaluation</h1>
<emu-grammar>ImportCall : `import` `(` AssignmentExpression `)`</emu-grammar>
<emu-alg>
1. Let _referencingScriptOrModule_ be ! GetActiveScriptOrModule().
1. Let _argRef_ be the result of evaluating |AssignmentExpression|.
1. Let _specifier_ be ? GetValue(_argRef_).
1. Let _promiseCapability_ be ! NewPromiseCapability(%Promise%).
1. Let _specifierString_ be <del>ToString</del> <ins>HostDynamicValueToSpecifier</ins>(<ins>_referencingScriptOrModule_, </ins>_specifier_).
1. IfAbruptRejectPromise(_specifierString_, _promiseCapability_).
1. <ins>Assert Type(_specifierString_) is String.</ins>
1. Perform ! HostImportModuleDynamically(_referencingScriptOrModule_, _specifierString_, _promiseCapability_).
1. Return _promiseCapability_.[[Promise]].
</emu-alg>
</emu-clause>
<emu-note type="editor">
<p>Second, this proposal defines the new host callout.</p>
</emu-note>
<emu-clause id="sec-hostdynamicvaluetospecifier" aoid="HostDynamicValueToSpecifier">
<h1><ins>Runtime Semantics: HostDynamicValueToSpecifier ( _referencingScriptOrModule_, _specifier_ )</ins></h1>
<p><ins>HostDynamicValueToSpecifier is an implementation-defined
abstract operation that converts a runtime value representing a
module specifier, _specifier_, to a string that corresponds to a
|ModuleSpecifier| String. The host also receives the Script
Record or Module Record, _referencingScriptOrModule_.
_referencingScriptOrModule_ may 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>
<p><ins>The default implementation performs the following steps:</ins></p>
<emu-alg>
1. <ins>Return ? ToString(_specifier_).</ins>
</emu-alg>
<p><ins>The implementation of HostDynamicValueToSpecifier must
conform to the following requirements:</ins></p>
<ul>
<li><ins>When HostDynamicValueToSpecifier completes normally, its result must
be a string.</ins></li>
<li><ins>HostDynamicValueToSpecifier may not stringify _specifier_ more
than once.</ins></li>
</ul>
</emu-clause>
</emu-clause>
<hr>
<h2 id="option2">Option 2: Reuse HostImportModuleDynamically</h1>
<emu-clause id="sec-import-calls">
<emu-note type="editor">
<p>Like the first option, this proposal adjusts `import(...)` to not stringify
its argument too early.
This means that HostImportModuleDynamically could pass given a
TrustedModuleSpecifier but not given a raw string.</p>
</emu-note>
<h1>Import Calls</h1>
<emu-clause id="sec-import-call-runtime-semantics-evaluation">
<h1>Runtime Semantics: Evaluation</h1>
<emu-grammar>ImportCall : `import` `(` AssignmentExpression `)`</emu-grammar>
<emu-alg>
1. Let _referencingScriptOrModule_ be ! GetActiveScriptOrModule().
1. Let _argRef_ be the result of evaluating |AssignmentExpression|.
1. Let _specifier_ be ? GetValue(_argRef_).
1. Let _promiseCapability_ be ! NewPromiseCapability(%Promise%).
1. <del>Let _specifierString_ be ToString(_specifier_).</del>
1. <del>IfAbruptRejectPromise(_specifierString_, _promiseCapability_).</del>
1. Perform ! HostImportModuleDynamically(_referencingScriptOrModule_, <del>_specifierString_</del><ins>_specifier_</ins>, _promiseCapability_).
1. Return _promiseCapability_.[[Promise]].
</emu-alg>
</emu-clause>
</emu-clause>
<hr>
<emu-clause id="sec-hostimportmoduledynamically" aoid="HostImportModuleDynamically">
<emu-note type="editor">
Second, this option adjusts HostImportModuleDynamically to deal with non-string
_specifier_s. It also threads the eventual string for of the specifier through to
FinishDynamicImport. This allows us to maintain the apparent order of operations.
</emu-note>
<h1>Runtime Semantics: HostImportModuleDynamically ( _referencingScriptOrModule_, _specifier_, _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 |ModuleSpecifier| String,
_specifier_, 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>
<ins>The abstract operation must perform ToString(_specifier_)
at most once. If it completes abruptly, the abstract operation
must take the failure path below.</ins>
</li>
<li>
<ins>
If the abstract operation performs ToString(_specifier_)
it must do so before returning control to the EcmaScript engine.
</ins>
<emu-note type="editor">
<p>This is necessary to prevent a different apparent order of operation
that could occur if user code received the module promise before
_specifier_ is stringified.</p>
<p>A non-conforming implementation might behave improperly for:</p>
<pre><code class="javascript">
const modulePromise = import({
toString() {
console.log('stringified');
return './foo';
},
};
console.log('got promise');
modulePromise.then(
() => { console.log('module resolved'); },
() => { console.log('module rejected'); });
// Before: stringified, got promise, module ...
// ⤩
// Non-conforming: got promise, stringified, module ...
</code></pre>
</emu-note>
</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_,
_specifier_, _promiseCapability_,
NormalCompletion(<del>*undefined*</del> <ins>_specifierString_</ins>))
<ins>where _specifierString_ is a String</ins>.
<ins>Normally, _specifierString_ is the result of stringifying _specifier_.</ins>
</li>
<li>Any subsequent call to HostResolveImportedModule after
FinishDynamicImport has completed, given the arguments
_referencingScriptOrModule_
and <del>_specifier_</del><ins>NormalCompletion(_specifierString_)</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>NormalCompletion(_specifierString_)</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_,
_specifier_, _promiseCapability_, an abrupt completion),
with the abrupt completion representing the cause of
failure.</li>
</ul>
</dd>
</dl>
</li>
<li>
<del>
Every call to HostImportModuleDynamically with the same
_referencingScriptOrModule_ and _specifier_ arguments must
conform to the <em>same</em> set of requirements above as
previous calls do. That is, if the host environment takes the
success path once for a given _referencingScriptOrModule_,
_specifier_ pair, it must always do so, and the same for the
failure path.
</del>
<emu-note type="editor">
<p>Since _specifier_ is not a string, the <em>same</em> would mean
less, but this is not necessary to preserve any single-fetch requirement
since FinishDynamicImport delegates to HostResolveImportModule which says</p>
<blockquote>
"This operation must be idempotent if it completes
normally. Each time it is called with a specific
_referencingScriptOrModule_, _specifier_ pair as arguments
it must return the same Module Record instance."
</blockquote>
</emu-note>
</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>
<hr>
<emu-clause id="sec-finishdynamicimport" aoid="FinishDynamicImport">
<emu-note type="editor">
<p>Finally, we adjust FinishDynamicImport to use the module specifier
string computed by the host callout.</p>
</emu-note>
<h1>Runtime Semantics: FinishDynamicImport ( _referencingScriptOrModule_, _specifier_, _promiseCapability_, _completion_ )</h1>
<p>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.</p>
<emu-alg>
1. If _completion_ is an abrupt completion, then perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « _completion_.[[Value]] »).
1. Else,
1. <del>Assert: _completion_ is a normal completion and _completion_.[[Value]] is *undefined*.</del>
1. <ins>Let _specifierString_ be ! _completion_.</ins>
1. <ins>Assert: Type(_specifierString_) is String.</ins>
1. Let _moduleRecord_ be ! HostResolveImportedModule(_referencingScriptOrModule_, <del>_specifier_</del><ins>_specifierString_</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]] »).
</emu-alg>
</emu-clause>