Skip to content

Commit fd68140

Browse files
committed
simplify: remove cyclic binding handling
1 parent 6e393ee commit fd68140

File tree

3 files changed

+46
-57
lines changed

3 files changed

+46
-57
lines changed

document/js-api/index.bs

Lines changed: 25 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df
156156
text: memory address; url: exec/runtime.html#syntax-memaddr
157157
text: global address; url: exec/runtime.html#syntax-globaladdr
158158
text: extern address; url: exec/runtime.html#syntax-externaddr
159+
text: extern subtype; url: valid/types.html#match-externtype
159160
text: page size; url: exec/runtime.html#page-size
160161
url: syntax/types.html#syntax-numtype
161162
text: i32
@@ -1163,7 +1164,7 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not
11631164
1. Let |values| be [=?=] [$IteratorToList$]([=?=] [$GetIteratorFromMethod$](|ret|, |method|)).
11641165
1. Let |wasmValues| be a new, empty [=list=].
11651166
1. If |values|'s [=list/size=] is not |resultsSize|, throw a {{TypeError}} exception.
1166-
1. [=list/iterate|For each=] |value| and |resultType| in |values| and |results|, paired linearly,
1167+
1. For each |value| and |resultType| in |values| and |results|, paired linearly,
11671168
1. [=list/Append=] [=ToWebAssemblyValue=](|value|, |resultType|) to |wasmValues|.
11681169
1. Return |wasmValues|.
11691170
</div>
@@ -1388,9 +1389,11 @@ To <dfn export>parse a WebAssembly module</dfn> given a <a>byte sequence</a> |by
13881389
1. If |module| is [=error=], throw a {{CompileError}} exception.
13891390
1. [=Construct a WebAssembly module object=] from |module| and |bytes|, and let |module| be the result.
13901391
1. Let |requestedModules| be a set.
1391-
1. [=list/iterate|For each=] (|moduleName|, <var ignore>name</var>, <var ignore>type</var>) in [=module_imports=](|module|.\[[Module]]),
1392+
1. For each (|moduleName|, <var ignore>name</var>, <var ignore>type</var>) in [=module_imports=](|module|.\[[Module]]),
13921393
1. [=set/Append=] |moduleName| to |requestedModules|.
13931394
1. Let |moduleRecord| be {
1395+
<!-- WebAssembly Module Records -->
1396+
\[[Instance]]: ~empty~,
13941397
<!-- Abstract Module Records -->
13951398
\[[Realm]]: |realm|,
13961399
\[[Environment]]: ~empty~,
@@ -1422,7 +1425,7 @@ The <dfn>export name list</dfn> of a WebAssembly Module Record |record| is defin
14221425

14231426
1. Let |module| be |record|'s \[[ModuleSource]] internal slot.
14241427
1. Let |exports| be an empty [=list=].
1425-
1. [=list/iterate|For each=] (|name|, <var ignore>type</var>) in [=module_exports=](|module|.\[[Module]])
1428+
1. For each (|name|, <var ignore>type</var>) in [=module_exports=](|module|.\[[Module]])
14261429
1. [=list/Append=] |name| to the end of |exports|.
14271430
1. Return |exports|.
14281431

@@ -1483,9 +1486,9 @@ WebAssembly Module Records have the following methods:
14831486
1. Let |resolutionName| be |resolution|.\[[BindingName]].
14841487
1. Let |externval| be [=instance_export=](|resolutionInstance|, |resolutionName|).
14851488
1. Assert: |externval| is not [=error=].
1486-
1. Assert: [=module_direct_exports=](|resolutionModule|) contains an element (|resolutionName|, <var ignore>type</var>).
1487-
1. Let |externtype| be the value of |type| for the element (|resolutionName|, |type|) in [=module_direct_exports=](|resolutionModule|).
1488-
1. If [=module_extern_subtype=](|externtype|, |importtype|) is false, throw a {{LinkError}} exception.
1489+
1. Assert: [=module_exports=](|resolutionModule|) contains an element (|resolutionName|, <var ignore>type</var>).
1490+
1. Let |externtype| be the value of |type| for the element (|resolutionName|, |type|) in [=module_exports=](|resolutionModule|).
1491+
1. If |importtype| is not an [=extern subtype=] of |externtype|, throw a {{LinkError}} exception.
14891492
1. [=list/Append=] |externval| to |imports|.
14901493
1. Otherwise,
14911494
1. Let |env| be |resolution|.\[[Module]].\[[Environment]].
@@ -1500,13 +1503,15 @@ WebAssembly Module Records have the following methods:
15001503
1. Let |externfunc| be the [=external value=] [=external value|func=] |funcaddr|.
15011504
1. [=list/Append=] |externfunc| to |imports|.
15021505
1. If |importtype| is of the form [=global=] |mut| |valtype|,
1506+
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
15031507
1. If |v| [=implements=] {{Global}},
15041508
1. Let |globaladdr| be |v|.\[[Global]].
1509+
1. Let |targetmut| <var ignore>valuetype</var> be [=global_type=](|store|, |globaladdr|).
1510+
1. If |mut| is [=const=] and |targetmut| is [=var=], throw a {{TypeError}}.
15051511
1. Otherwise,
1506-
1. If |valtype| is [=v128=],
1507-
1. Throw a {{LinkError}} exception.
1512+
1. If |valtype| is [=v128=], throw a {{LinkError}} exception.
1513+
1. If |mut| is [=var=], throw a {{TypeError}}.
15081514
1. Let |value| be [=?=] [=ToWebAssemblyValue=](|v|, |valtype|).
1509-
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
15101515
1. Let (|store|, |globaladdr|) be [=global_alloc=](|store|, |mut| |valtype|, |value|).
15111516
1. Set the [=surrounding agent=]'s [=associated store=] to |store|.
15121517
1. Let |externglobal| be [=external value|global=] |globaladdr|.
@@ -1522,53 +1527,34 @@ WebAssembly Module Records have the following methods:
15221527
1. [=list/Append=] |externtable| to |imports|.
15231528
1. [=Instantiate the core of a WebAssembly module=] |module| with |imports|, and let |instance| be the result.
15241529
1. Set |record|.\[[Instance]] to |instance|.
1525-
1. [=list/iterate|For each=] (|name|, |externtype|) of [=module_direct_exports=](|module|),
1526-
1. Let |externval| be [=instance_export=](|instance|, |name|).
1527-
1. Assert: |externval| is not [=error=].
1528-
1. If |externtype| is of the form [=func=] <var ignore>functype</var>,
1529-
1. Assert: |externval| is of the form [=external value|func=] |funcaddr|.
1530-
1. Let [=external value|func=] |funcaddr| be |externval|.
1531-
1. Let |func| be the result of creating [=a new Exported Function=] from |funcaddr|.
1532-
1. Perform [=!=] |record|.\[[Environment]].InitializeBinding(|name|, |func|).
1530+
1. [=list/iterate|For each=] (|name|, |externtype|) of [=module_exports=](|module|),
15331531
1. If |externtype| is of the form [=global=] |mut| |globaltype|,
15341532
1. Assert: |externval| is of the form [=external value|global=] |globaladdr|.
15351533
1. Let [=external value|global=] |globaladdr| be |externval|.
15361534
1. Let |global_value| be [=global_read=](|store|, |globaladdr|).
15371535
1. If |globaltype| is not [=v128=],
1538-
1. Note: When integrating with shared globals, they will be excluded here similarly to v128 above.
1536+
1. Note: The condition above leaves unsupported JS values as uninitialized in TDZ and therefore as a reference error on
1537+
access. When integrating with shared globals, they may be excluded here similarly to v128 above.
15391538
1. Perform [=!=] |record|.\[[Environment]].InitializeBinding(|name|, [=ToJSValue=](|global_value|)).
1540-
1. Associate all future mutations to the mutable value at |globaladdr| with the ECMA-262 binding record for |name| in
1539+
1. If |mut| is [=var=], then associate all future mutations of |globaladdr| with the ECMA-262 binding record for |name| in
15411540
|record|.\[[Environment]], such that |record|.\[[Environment]].GetBindingValue(|resolution|.\[[BindingName]], true)
15421541
always returns [=ToJSValue=]([=global_read=](|store|, |globaladdr|)) for the current [=surrounding agent=]'s
15431542
[=associated store=] |store|.
1544-
1. If |externtype| is of the form [=mem=] <var ignore>memtype</var>,
1545-
1. Assert: |externval| is of the form [=external value|mem=] |memaddr|.
1546-
1. Let [=external value|mem=] |memaddr| be |externval|.
1547-
1. Let |memory| be [=create a memory object|a new Memory object=] created from |memaddr|.
1548-
1. Perform [=!=] |record|.\[[Environment]].InitializeBinding(|name|, |memory|).
1549-
1. If |externtype| is of the form [=table=] <var ignore>tabletype</var>,
1550-
1. Assert: |externval| is of the form [=external value|table=] |tableaddr|.
1551-
1. Let [=external value|table=] |tableaddr| be |externval|.
1552-
1. Let |table| be [=create a Table object|a new Table object=] created from |tableaddr|.
1553-
1. Perform [=!=] |record|.\[[Environment]].InitializeBinding(|name|, |table|).
1554-
1555-
Note: The export bindings for a WebAssembly Module Record are designed to mirror the live bindings of all directly exported values. Values not
1556-
supported in JS are left uninitialized and in TDZ, while still being accesible to Wasm importers. Indirect exports (re-exports) referencing
1557-
JavaScript values will capture the [=ToWebAssemblyValue=] interpretation at the time of execution, while indirect exports resolving to other Wasm
1558-
modules will support Wasm live bindings. For exported mutable globals that are supported in JavaScript, the live bindings on the environment record
1559-
will always reflect the current mutable global export value as interpreted through the infallible [=ToJSValue=] interpretation.
1543+
1. Otherwise,
1544+
1. Perform ! |record|.\[[Environment]].InitializeBinding(|name|, ! Get(|instance|.\[[Exports]], |name|)).
15601545

15611546
</div>
15621547

15631548
<h3 id="hostgetmodulesourcemodulerecord">HostGetModuleSourceModuleRecord ( |specifier| )</h3>
15641549
1. If |specifier| is a WebAssembly {{Module}} object with a \[[Module]] internal slot,
1565-
1. Let |module| be |specifier|.\[[Module]].
1566-
1. If |module|.\[[ModuleRecord]] is null,
1550+
1. If |specifier|.\[[Module]].\[[ModuleRecord]] is null,
15671551
1. Let |realm| be the current agent's realm record.
15681552
1. Let |requestedModules| be a set.
1569-
1. [=list/iterate|For each=] (|moduleName|, <var ignore>name</var>, <var ignore>type</var>) in [=module_imports=](|module|),
1553+
1. For each (|moduleName|, <var ignore>name</var>, <var ignore>type</var>) in [=module_imports=](|specifier|.\[[Module]]),
15701554
1. [=set/Append=] |moduleName| to |requestedModules|.
15711555
1. Let |moduleRecord| be {
1556+
<!-- WebAssembly Module Records -->
1557+
\[[Instance]]: ~empty~,
15721558
<!-- Abstract Module Records -->
15731559
\[[Realm]]: |realm|,
15741560
\[[Environment]]: ~empty~,
@@ -1591,7 +1577,7 @@ will always reflect the current mutable global export value as interpreted throu
15911577
}.
15921578
1. Set |module|.\[[ModuleRecord]] to |moduleRecord|.
15931579
1. Return |moduleRecord|.
1594-
1. Return |module|.\[[ModuleRecord]].
1580+
1. Return |specifier|.\[[Module]].\[[ModuleRecord]].
15951581
1. Return ~not-a-source~.
15961582

15971583
Note: See corresponding modifications to HTML in <a href="https://github.com/whatwg/html/pull/10380">PR #10380</a>.

proposals/esm-integration/EXAMPLES.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,17 @@ for (let index = 0; index < length; index++)
123123
| table | `WebAssembly.Table` object |
124124
| function | WebAssembly exported function |
125125

126+
Wasm bindings cannot be reassigned as it can in JS, so the exported value will not change in their object identity. But the value that it points to (e.g. `.value` in the case of `WebAssembly.Global`) can change.
127+
126128
1. JS module is parsed
127129
1. wasm module is parsed
128130
1. wasm module has a lexical environment created for its exports. All exports are initially in TDZ.
129131
1. JS module is instantiated. Imports are bound to the same memory locations.
130132
1. wasm module is instantiated evaluated. Functions are initialized. Memories and tables are initialized and filled with data/elem sections. Globals are initialized and initializer expressions are evaluated. The start function runs.
131133
1. JS module is evaluated. All values are available.
132134

135+
The value of the export for `WebAssembly.Global` is provided directly on the JS namespace object, with mutable globals reflected as live bindings to JS.
136+
133137
#### Example
134138

135139
```js
@@ -177,7 +181,7 @@ Wasm exports can be imported as accurate, immutable bindings to other wasm modul
177181

178182
### wasm imports <- JS re-exports <- wasm exports
179183

180-
Any wasm exports that are re-exported via a JS module will be available to the other wasm module as accurate bindings. If the binding is a mutable global, then that binding will be a live export binding in JS reflecting changes to the Wasm global value. When imported from JS, the first and second Wasm modules will yield the same Wasm identities for the multiply exported Wasm objects.
184+
Any wasm exports that are re-exported via a JS module will be available to the other wasm module as accurate, immutable bindings. The wasm export gets wrapped into a JS object (e.g. `WebAssembly.Global`) and then unwrapped to the wasm import in the importing wasm module. When imported from JS, the first and second Wasm modules will yield the same object identities for the multiply exported Wasm objects.
181185

182186
#### Example
183187

@@ -213,7 +217,7 @@ export {memoryExport} from "./b.wasm";
213217

214218
1. JS module is parsed
215219
1. wasm module is parsed
216-
1. wasm module has a lexical environment created for its exports. All direct exports are initially in TDZ, indirect exports are available as they resolve.
220+
1. wasm module has a lexical environment created for its exports. All exports are initially in TDZ.
217221
1. JS module is instantiated. All imports (including functions) from the wasm module are memory locations holding undefined.
218222
1. wasm module is instantiated and evaluated. Snapshots of imports are taken. Export bindings are initialized.
219223
1. JS module is evaluated.
@@ -251,8 +255,8 @@ export function functionExport() {
251255
1. wasm module is parsed
252256
1. JS module is parsed
253257
1. JS module is instantiated.
254-
1. wasm module has a lexical environment created for its exports. All direct exports are initially in TDZ, indirect exports are available as they resolve.
255-
1. JS module is evaluated. wasm exports in TDZ lead to a ReferenceError if used.
258+
1. wasm module has a lexical environment created for its exports. All exports are initially in TDZ.
259+
1. JS module is evaluated. wasm exports lead to a ReferenceError if used.
256260
1. wasm module is instantiated and evaluated; wasm-exported bindings are updated to their appropriate JS API-exposed values.
257261

258262
#### Examples

proposals/esm-integration/README.md

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -94,22 +94,23 @@ In the proposed HTML integration of WebAssembly modules, the [module name](https
9494

9595
### "Snapshotting" imports
9696

97-
When imports are provided to WebAssembly modules in the host instance linking model from JavaScript, they are provided directly upfront.
97+
When imports are provided to WebAssembly modules in the host instance linking model, they are provided directly upfront.
9898

9999
- This handling of imports could be called a "snapshotting" process: Later updates to the imported values won't be reflected within the WebAssembly module.
100-
- Circular WebAssembly module bindings are not supported for direct exports: One of them will run first, and that one will find that the exports of the other aren't yet initialized, leading to a ReferenceError. Indirect exports (reexports) can be supported early in Wasm cycles, where a WebAssembly module exports the same binding it imports.
100+
- Circular WebAssembly modules are not supported: One of them will run first, and that one will find that the exports of the other aren't yet initialized, leading to a ReferenceError.
101101

102102
See the FAQ for more explanation of the rationale for this design decision, and what features it enables which would be difficult or impossible otherwise.
103103

104-
### Live binding exports
104+
### Progressive Implementation Support
105105

106-
WebAssembly modules support live exports bindings for mutable globals by reflecting the infallible ToJsValue() representation of their mutable global exports on their JavaScript environment bindings record.
106+
It is possible to implement the Wasm-ESM integration in two stages:
107107

108-
For bindings that are exported from bindings that were initially imported i.e. indirect exports or reexports:
108+
1. In the first stage only source phase imports of Wasm are supported (`import source fibModule from "./fib.wasm"`).
109+
2. In the second stage, evaluation phase imports would be supported too (`import { fib } from "./fib.wasm"`).
109110

110-
1. WebAssembly modules that have indirect exports to JavaScript modules, these values are snapshotted at the time of execution of the WebAssembly module and reflected as captured bindings and not live bindings on the exports, even if the underlying JavaScript module might have modifications to the binding later on.
111+
If initially implementing just source phase imports, the `GetExportedNames`, `ResolveExport`, `InitializeEnvironment`, and `ExecuteModule` abstract operations can be implemented as abstract operations unconditionally throwing a `SyntaxError` exception. In this case, module fetch and CSP integration is still required to be implemented as specified in this proposal.
111112

112-
2. WebAssembly modules that have indirect exports to WebAssembly modules, where those modules directly export mutable globals, support the live mutable global reference without capturing the import.
113+
Implementers are encouraged to ship both stages at once, but it is deemed OK for implementers to initially ship the first stage and then quickly follow up with the second stage, if this aids "time to ship" in implementations.
113114

114115
## FAQ
115116

@@ -119,7 +120,7 @@ Originally the ESM integration only provided the direct host instance linking mo
119120

120121
Supporting the [source phase](https://github.com/tc39/proposal-source-phase-imports) ESM integration is therefore a more general form of the ESM integration that shares the host resolver, while retaining linking flexibility for Js host embedding of Wasm.
121122

122-
While the source phase does not replace the instance linking model, is does offer a more general and flexible ESM integration as an addition.
123+
While the source phase does not replace the instance linking model, is does offer a more general and flexible ESM integration.
123124

124125
### How does this relate to the Component Model?
125126

@@ -149,7 +150,7 @@ The conversion is based on the type that the import and export was declared as,
149150

150151
### When one WebAssembly module imports another one, will there be overhead due to converting back and forth to JS values?
151152

152-
Bindings between WebAssembly modules, even those that are indirect through indirect exports (reexports) are directly linked, without needing to go through JS wrapping and unwrapping.
153+
Note that exports of ES Module Records always have values that can be directly treated as JavaScript values. Although we're talking about conversions to and from JavaScript for these exports, it's expected that, in native implementations, the conversion to and from Javascript would "cancel out" and not lead to the use of wrappers in practice.
153154

154155
### Why are WebAssembly modules instantiated during the "Evaluation" phase?
155156

@@ -171,11 +172,9 @@ Instead of including this check in the default semantics of functions, a trampol
171172

172173
### What does snapshotting imports mean for importing globals?
173174

174-
Imports are only snapshotted when they resolve to a JavaScript module. For imports that resolve to WebAssembly modules, these are always directly bound.
175-
176-
When a WebAssembly module imports a Global, that resolves to a module after resolving the binding through any indirect exports (reexports):
177-
- If the resolved module is a JavaScript module, then the exporting module may either export a direct value or a `WebAssembly.Global` of the same type.
178-
- If the resolved module is a WebAssembly module, then the exporting module must export a global that is an extern subtype of the importing global.
175+
When a WebAssembly module imports a Global, there are two possible modes of operation:
176+
- If the Global type is immutable (as declared in the importing module), then the exporting module may either export a numeric value or an immutable Global.
177+
- If the Global type is mutable, then the exporting module must export a mutable Global. The snapshot here is "shallow" in the sense that modifications *within* this particular mutable Global object *will* be visible in the importing module (but, if the exporting module overwrites the entire binding with some unrelated value, this will not be noticed by the importing module).
179178

180179
### Can Web APIs be imported via modules?
181180

0 commit comments

Comments
 (0)