-
Notifications
You must be signed in to change notification settings - Fork 20
/
spec.emu
294 lines (257 loc) · 13.8 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
<!doctype html>
<meta charset="utf8">
<script src="ecmarkup.js"></script>
<link rel="stylesheet" href="ecmarkup.css">
<title>JSON modules</title>
<pre class=metadata>
title: JSON modules
status: proposal
stage: 3
location: https://github.com/tc39/proposal-json-modules
copyright: false
contributors: Sven Sauleau, Myles Borins, Daniel Ehrenberg, Daniel Clark
</pre>
<emu-intro id="intro">
<h1>JSON modules</h1>
<p>See <a href="https://github.com/tc39/proposal-json-modules/blob/master/README.md">the explainer</a> for information.</p>
<emu-note type="editor">
<p>This proposal is buit on top of the <a href="https://tc39.es/proposal-import-attributes">Import Attributes</a> proposal.</p>
</emu-note>
</emu-intro>
<emu-clause id="sec-semantics">
<h1>Semantics</h1>
<emu-clause id="sec-HostLoadImportedModule" type="host-defined abstract operation">
<h1>
HostLoadImportedModule (
_referrer_: a Script Record, a Cyclic Module Record, or a Realm Record,
_moduleRequest_: a ModuleRequest Record,
_hostDefined_: anything,
_payload_: a GraphLoadingState Record or a PromiseCapability Record,
): ~unused~
</h1>
<dl class="header">
<dt>description</dt>
<dd></dd>
</dl>
<emu-note id="note-HostLoadImportedModule-referrer-Realm-Record">
<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"><button type="button" onclick="import('./foo.mjs')">Click me</button></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>An implementation of HostLoadImportedModule must conform to the following requirements:</p>
<ul>
<li>
The host environment must perform FinishLoadingImportedModule(_referrer_, _moduleRequest_, _payload_, _result_), where _result_ is either a normal completion containing the loaded Module Record or a throw completion, either synchronously or asynchronously.
</li>
<li>
<p>If this operation is called multiple times with two (_referrer_, _moduleRequest_) pairs such that:</p>
<ul>
<li>the first _referrer_ is the same as the second _referrer_;</li>
<li>the first _moduleRequest_.[[Specifier]] is the same as the second _moduleRequest_.[[Specifier]];</li>
<li>ImportAttributesEqual(the first _moduleRequest_.[[Attributes]], the second _moduleRequest_.[[Attributes]]) is *true*;</li>
<li>it performs FinishLoadingImportedModule(_referrer_, _moduleRequest_, _payload_, _result_) where _result_ is a normal completion,</li>
</ul>
<p>then it must perform FinishLoadingImportedModule(_referrer_, _moduleRequest_, _payload_, _result_) with the same _result_ each time.</p>
</li>
<li>
<p><ins>If _moduleRequest_.[[Attributes]] has an entry _entry_ such that _entry_.[[Key]] is *"type"* and _entry_.[[Value]] is *"json"*, the host environment must perform FinishLoadingImportedModule(_referrer_, _moduleRequest_, _payload_, _result_), where _result_ is either the Completion Record returned by an invokation of ParseJSONModule or a throw completion.</ins></p>
</li>
<li>
The operation must treat _payload_ as an opaque value to be passed through to FinishLoadingImportedModule.
</li>
</ul>
<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_, _moduleRequest_.[[Specifer]], _moduleRequest_.[[Attributes]]) triples may map to the same Module Record instance. The actual mapping semantics 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 expansion of relative and abbreviated path specifiers.</p>
<emu-note>
<p><ins>The above text implies that hosts *must* support JSON modules imported with `type: "json"` (if it completes normally), but it doesn't prohibit hosts from supporting JSON modules imported with no type specified. Some environments (for example, web browsers) plan to require `with { type: "json" }`, and environments which want to restrict themselves to a compatible subset would do so as well.</ins></p>
</emu-note>
<emu-note>
<p><ins>All of the import statements in the module graph that address the same JSON module may evaluate to the same mutable object.</ins></p>
</emu-note>
</emu-clause>
<emu-clause id="sec-synthetic-module-records">
<h1>Synthetic Module Records</h1>
<p>A <dfn variants="Synthetic Module Records">Synthetic Module Record</dfn> is used to represent information about a module that is defined by specifications. Its exports are derived from a pair of lists, of string keys and of ECMAScript values. The set of exported names is static, and determined at creation time (as an argument to CreateSyntheticModule), while the set of exported values can be changed over time using SetSyntheticModuleExport. It has no imports or dependencies.</p>
<emu-note>A Synthetic Module Record could be used for defining a variety of module types: for example, built-in modules, or JSON modules, or CSS modules.</emu-note>
<p>In addition to the fields defined in <emu-xref href="#table-module-record-fields"></emu-xref> Synthetic Module Records have the additional fields listed in <emu-xref href="#table-synthetic-module-record-fields"></emu-xref>. Each of these fields is initially set in CreateSyntheticModule.</p>
<emu-table id="table-synthetic-module-record-fields" caption="Additional Fields of Synthetic Module Records">
<table>
<thead>
<th>Field Name</th>
<th>Value Type</th>
<th>Meaning</th>
</thead>
<tbody>
<tr>
<td>[[ExportNames]]</td>
<td>List of String</td>
<td>A List of all names that are exported.</td>
</tr>
<tr>
<td>[[EvaluationSteps]]</td>
<td>An Abstract Closure</td>
<td>An Abstract Closure that will be performed upon evaluation of the module, taking the Synthetic Module Record as its sole argument. These will usually set up the exported values, by using SetSyntheticModuleExport. They must not modify [[ExportNames]]. They may return an abrupt completion.</td>
</tr>
</tbody>
</table>
</emu-table>
<emu-clause id="sec-createsyntheticmodule" type="abstract operation">
<h1>
CreateSyntheticModule (
_exportNames_: a List of Strings without duplicates,
_evaluationSteps_: an Abstract Closure,
_realm_: a Realm Record,
): a Synthetic Module Record
</h1>
<dl class="header">
<dt>description</dt>
<dd>It creates a Synthetic Module Record based upon the given exported names and evaluation steps.</dd>
</dl>
<emu-alg>
1. Return Synthetic Module Record { [[Realm]]: _realm_, [[Environment]]: ~empty~, [[Namespace]]: ~empty~, [[HostDefined]]: ~empty~, [[ExportNames]]: _exportNames_, [[EvaluationSteps]]: _evaluationSteps_ }.
</emu-alg>
<emu-note type="editor">It seems we could set up the environment either here or in Link(). I've chosen to do so in Link() for symmetry with Cyclic Module Records (and thus Source Text Module Records), but I don't think there's any actual requirement in that regard.</emu-note>
</emu-clause>
<emu-clause id="sec-setsyntheticmoduleexport" type="abstract operation">
<h1>
SetSyntheticModuleExport (
_module_: a Synthetic Module Record,
_exportName_: a String,
_exportValue_: an ECMAScript Language value
): ~unused~
</h1>
<dl class="header">
<dt>description</dt>
<dd>It can be used to set or change the exported value for a pre-established export of a Synthetic Module Record.</dd>
</dl>
<emu-alg>
1. Let _envRec_ be _module_.[[Environment]].
1. Assert: _envRec_ is not ~empty~.
1. Perform _envRec_.SetMutableBinding(_exportName_, _exportValue_, *true*).
1. Return ~unused~.
</emu-alg>
</emu-clause>
<emu-clause id="sec-smr-concrete-methods">
<h1>Concrete Methods</h1>
<p>The following are the concrete methods for Synthetic Module Record that implement the corresponding Module Record abstract methods.</p>
<emu-note type="editor">I find having this wrapping sub-clause cleaner and suggest we do the same for Source Text Module Records in the main spec.</emu-note>
<emu-clause id="sec-smr-LoadRequestedModules" type="concrete method">
<h1>
LoadRequestedModules (): a Promise
</h1>
<dl class="header">
<dt>for</dt>
<dd>a Synthetic Module Record _module_</dd>
</dl>
<emu-alg>
1. Return ! PromiseResolve(%Promise%, *undefined*).
</emu-alg>
<emu-note>
Synthetic Module Records have no dependencies.
</emu-note>
</emu-clause>
<emu-clause id="sec-smr-getexportednames" type="concrete method">
<h1>
GetExportedNames (): a List of Strings
</h1>
<dl class="header">
<dt>for</dt>
<dd>a Synthetic Module Record _module_</dd>
</dl>
<emu-alg>
1. Return _module_.[[ExportNames]].
</emu-alg>
</emu-clause>
<emu-clause id="sec-smr-resolveexport" type="concrete method">
<h1>
ResolveExport(
_exportName_: a String,
): a ResolvedBinding Record or *null*
</h1>
<dl class="header">
<dt>for</dt>
<dd>a Synthetic Module Record _module_</dd>
</dl>
<emu-alg>
1. If _module_.[[ExportNames]] does not contain _exportName_, return *null*.
1. Return ResolvedBinding Record { [[Module]]: _module_, [[BindingName]]: _exportName_ }.
</emu-alg>
</emu-clause>
<emu-clause id="sec-smr-Link" type="concrete method">
<h1>Link ( ): ~unused~</h1>
<dl class="header">
<dt>for</dt>
<dd>a Synthetic Module Record _module_</dd>
</dl>
<emu-alg>
1. Let _realm_ be _module_.[[Realm]].
1. Let _env_ be NewModuleEnvironment(_realm_.[[GlobalEnv]]).
1. Set _module_.[[Environment]] to _env_.
1. For each String _exportName_ in _module_.[[ExportNames]], do
1. Perform ! _env_.CreateMutableBinding(_exportName_, *false*).
1. Perform ! _env_.InitializeBinding(_exportName_, *undefined*).
1. Return ~unused~.
</emu-alg>
</emu-clause>
<emu-clause id="sec-smr-Evaluate" type="concrete method">
<h1>Evaluate ( ): a Promise</h1>
<dl class="header">
<dt>for</dt>
<dd>a Synthetic Module Record _module_</dd>
</dl>
<emu-alg>
1. Let _moduleContext_ be a new ECMAScript code execution context.
1. Set the Function of _moduleContext_ to *null*.
1. Set the Realm of _moduleContext_ to _module_.[[Realm]].
1. Set the ScriptOrModule of _moduleContext_ to _module_.
1. Set the VariableEnvironment of _moduleContext_ to _module_.[[Environment]].
1. Set the LexicalEnvironment of _moduleContext_ to _module_.[[Environment]].
1. Suspend the currently running execution context.
1. Push _moduleContext_ on to the execution context stack; _moduleContext_ is now the running execution context.
1. Let _steps_ be _module_.[[EvaluationSteps]].
1. Let _result_ be Completion(_steps_(_module_)).
1. Suspend _moduleContext_ 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. Let _pc_ be ! NewPromiseCapability(%Promise%).
1. IfAbruptRejectPromise(_result_, _pc_).
1. Perform ! _pc_.[[Resolve]](_result_).
1. Return _pc_.[[Promise]].
</emu-alg>
</emu-clause>
</emu-clause>
<emu-clause id="sec-create-default-export-synthetic-module" type="abstract operation">
<h1>
CreateDefaultExportSyntheticModule (
_defaultExport_: an ECMAScript Language value,
): a Synthetic Module Record
</h1>
<dl class="header">
<dt>description</dt>
<dd>It creates a Synthetic Module Record whose default export is _defaultExport_.</dd>
</dl>
<emu-alg>
1. Let _realm_ be the current Realm Record.
1. Let _setDefaultExport_ be a new Abstract Closure with parameters (_module_) that captures _defaultExport_ and performs the following steps when called:
1. Perform SetSyntheticModuleExport(_module_, *"default"*, _defaultExport_).
1. Return CreateSyntheticModule(« *"default"* », _setDefaultExport_, _realm_).
</emu-alg>
</emu-clause>
<emu-clause id="sec-parse-json-module" type="abstract operation">
<h1>
ParseJSONModule (
_source_: a String
): either a normal completion containing a Synthetic Module Record, or a throw completion
</h1>
<dl class="header">
<dt>description</dt>
<dd></dd>
</dl>
<emu-alg>
1. Let _json_ be ? Call(%JSON.parse%, *undefined*, « _source_ »).
1. Return CreateDefaultExportSyntheticModule(_json_).
</emu-alg>
<emu-note type="editor">
TODO: Refactor JSONParse into an abstract algorithm.
</emu-note>
</emu-clause>
</emu-clause>
</emu-clause>