Skip to content

Commit 03354b6

Browse files
authored
Merge pull request #104 from WebAssembly/module-bindings
Support direct global bindings
2 parents 4bc248e + ef477aa commit 03354b6

File tree

3 files changed

+106
-23
lines changed

3 files changed

+106
-23
lines changed

document/js-api/index.bs

+92-17
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT
7979
text: CreateArrayFromList; url: sec-createarrayfromlist
8080
text: Cyclic Module Record; url: cyclic-module-record
8181
text: GetMethod; url: sec-getmethod
82-
text: ToBigInt64; url: #sec-tobigint64
82+
text: ToBigInt64; url: sec-tobigint64
83+
text: Module Namespace exotic object; url: sec-module-namespace-exotic-objects
84+
text: ResolvedBinding Record; url: resolvedbinding-record
8385
type: abstract-op
8486
text: CreateDataPropertyOrThrow; url: sec-createdatapropertyorthrow
8587
text: CreateMethodProperty; url: sec-createmethodproperty
@@ -156,6 +158,7 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df
156158
text: memory address; url: exec/runtime.html#syntax-memaddr
157159
text: global address; url: exec/runtime.html#syntax-globaladdr
158160
text: extern address; url: exec/runtime.html#syntax-externaddr
161+
text: extern subtype; url: valid/types.html#match-externtype
159162
text: page size; url: exec/runtime.html#page-size
160163
url: syntax/types.html#syntax-numtype
161164
text: i32
@@ -317,6 +320,8 @@ namespace WebAssembly {
317320

318321
Promise<Instance> instantiate(
319322
Module moduleObject, optional object importObject);
323+
324+
Instance namespaceInstance(ModuleNamespace moduleNamespace);
320325
};
321326
</pre>
322327

@@ -328,6 +333,7 @@ Note:
328333
WebAssembly.compile(|bytes|) asynchronously validates and complies bytes of WebAssembly into a Module.
329334
WebAssembly.instantiate(|bytes|, |importObject|) asynchronously compiles and instantiates a WebAssembly module from bytes of source.
330335
The WebAssembly.instantiate(|moduleObject|, |importObject|) asynchronously instantiates a compiled module.
336+
WebAssembly.namespaceInstance(|namespace|) obtains the WebAssembly Instance from the Module Namespace Exotic Object for a WebAssembly Module Record.
331337
-->
332338

333339
<div algorithm>
@@ -535,6 +541,14 @@ The verification of WebAssembly type requirements is deferred to the
535541

536542
Note: A follow-on streaming API is documented in the <a href="https://webassembly.github.io/spec/web-api/index.html">WebAssembly Web API</a>.
537543

544+
<div algorithm>
545+
The <dfn method for="WebAssembly">namespaceInstance(|namespace|)</dfn> method, when invoked, performs the following steps:
546+
1. Assert: |namespace| is a [=Module Namespace exotic object=].
547+
1. If |namespace|.\[[Module]] is not a [=WebAssembly Module Record=], [=throw=] a {{TypeError}} exception.
548+
1. Let |module| be |namespace|.\[[Module]].
549+
1. Return |module|.\[[Instance]].
550+
</div>
551+
538552
<h3 id="modules">Modules</h3>
539553

540554
<pre class="idl">
@@ -1391,6 +1405,8 @@ To <dfn export>parse a WebAssembly module</dfn> given a <a>byte sequence</a> |by
13911405
1. For each (|moduleName|, <var ignore>name</var>, <var ignore>type</var>) in [=module_imports=](|module|.\[[Module]]),
13921406
1. [=set/Append=] |moduleName| to |requestedModules|.
13931407
1. Let |moduleRecord| be {
1408+
<!-- WebAssembly Module Records -->
1409+
\[[Instance]]: ~empty~,
13941410
<!-- Abstract Module Records -->
13951411
\[[Realm]]: |realm|,
13961412
\[[Environment]]: ~empty~,
@@ -1470,25 +1486,82 @@ WebAssembly Module Records have the following methods:
14701486
<h3 id="module-execution">ExecuteModule ( [ |promiseCapability| ] ) Concrete Method</h3>
14711487
1. Assert: |promiseCapability| was not provided.
14721488
1. Let |record| be this WebAssembly Module Record.
1473-
1. Let |module| be |record|.\[[ModuleSource]].
1474-
1. Let |imports| be a new, empty [=map=].
1475-
1. For each (|importedModuleName|, |name|, <var ignore>type</var>) in [=module_imports=](|module|.\[[Module]]),
1476-
1. If |imports|[|importedModuleName|] does not exist, set |imports|[|importedModuleName|] to a new, empty [=map=].
1489+
1. Let |module| be |record|.\[[ModuleSource]].\[[Module]].
1490+
1. Let |imports| be « ».
1491+
1. [=list/iterate|For each=] (|importedModuleName|, |name|, |importtype|) in [=module_imports=](|module|),
14771492
1. Let |importedModule| be [$GetImportedModule$](|record|, |importedModuleName|).
1478-
1. Let |value| be ? |importedModule|.\[[Environment]].GetBindingValue(|name|, true).
1479-
1. Set |imports|[|importedModuleName|][|name|] to |value|.
1480-
1. Let |importObject| be ! [$OrdinaryObjectCreate$](null).
1481-
1. For each |key| → |value| of |imports|,
1482-
1. Let |moduleImportsObject| be ! [$OrdinaryObjectCreate$](null).
1483-
1. For each |importedName| → |importedValue| of |value|,
1484-
1. Perform ! [$CreateDataPropertyOrThrow$](|moduleImportsObject|, |importedName|, |importedValue|).
1485-
1. Perform ! [$CreateDataPropertyOrThrow$](|importObject|, |key|, |moduleImportsObject|).
1486-
1. [=Read the imports=] of |module| with imports |importObject|, and let |imports| be the result.
1493+
1. Let |resolution| be |importedModule|.ResolveExport(|name|).
1494+
1. Assert: |resolution| is a [=ResolvedBinding Record=], as validated during environment initialization.
1495+
1. Let |resolvedModule| be |resolution|.\\[[Module]].
1496+
1. Let |resolvedName| be |resolution|.\[[BindingName]].
1497+
1. If |resolvedModule| is a WebAssembly Module Record,
1498+
1. If |resolvedModule|.\[[Instance]] is ~empty~, throw a {LinkError} exception.
1499+
1. Assert: |resolvedModule|.\[[Instance]] is a WebAssembly {{Instance}} object.
1500+
1. Assert: |resolvedModule|.\[[ModuleSource]] is a WebAssembly {{Module}} object.
1501+
1. Let |module| be |resolvedModule|.\[[ModuleSource]].\[[Module]].
1502+
1. Let |externval| be [=instance_export=](|resolvedModule|.\[[Instance]], |resolvedName|).
1503+
1. Assert: |externval| is not [=error=].
1504+
1. Assert: [=module_exports=](|module|) contains an element (|resolvedName|, <var ignore>type</var>).
1505+
1. Let |externtype| be the value of |type| for the element (|resolvedName|, |type|) in [=module_exports=](|module|).
1506+
1. If |importtype| is not an [=extern subtype=] of |externtype|, throw a {{LinkError}} exception.
1507+
1. [=list/Append=] |externval| to |imports|.
1508+
1. Otherwise,
1509+
1. Let |env| be |resolvedModule|.\[[Environment]].
1510+
1. Let |v| be [=?=] |env|.GetBindingValue(|resolvedName|, true).
1511+
1. If |importtype| is of the form [=func=] |functype|,
1512+
1. If [$IsCallable$](|v|) is false, throw a {{LinkError}} exception.
1513+
1. If |v| has a \[[FunctionAddress]] internal slot, and therefore is an [=Exported Function=],
1514+
1. Let |funcaddr| be the value of |v|'s \[[FunctionAddress]] internal slot.
1515+
1. Otherwise,
1516+
1. [=Create a host function=] from |v| and |functype|, and let |funcaddr| be the result.
1517+
1. Let <var ignore>index</var> be the number of external functions in |imports|, defining the [=index of the host function=] |funcaddr|.
1518+
1. Let |externfunc| be the [=external value=] [=external value|func=] |funcaddr|.
1519+
1. [=list/Append=] |externfunc| to |imports|.
1520+
1. If |importtype| is of the form [=global=] |mut| |valtype|,
1521+
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
1522+
1. If |v| [=implements=] {{Global}},
1523+
1. Let |globaladdr| be |v|.\[[Global]].
1524+
1. Let |targetmut| <var ignore>valuetype</var> be [=global_type=](|store|, |globaladdr|).
1525+
1. If |mut| is [=const=] and |targetmut| is [=var=], throw a {{LinkError}} exception.
1526+
1. Otherwise,
1527+
1. If |valtype| is [=v128=], throw a {{LinkError}} exception.
1528+
1. If |mut| is [=var=], throw a {{LinkError}} exception.
1529+
1. Let |value| be [=?=] [=ToWebAssemblyValue=](|v|, |valtype|).
1530+
1. Let (|store|, |globaladdr|) be [=global_alloc=](|store|, |mut| |valtype|, |value|).
1531+
1. Set the [=surrounding agent=]'s [=associated store=] to |store|.
1532+
1. Let |externglobal| be [=external value|global=] |globaladdr|.
1533+
1. [=list/Append=] |externglobal| to |imports|.
1534+
1. If |importtype| is of the form [=mem=] <var ignore>memtype</var>,
1535+
1. If |v| does not [=implement=] {{Memory}}, throw a {{LinkError}} exception.
1536+
1. Let |externmem| be the [=external value=] [=external value|mem=] |v|.\[[Memory]].
1537+
1. [=list/Append=] |externmem| to |imports|.
1538+
1. If |importtype| is of the form [=table=] <var ignore>tabletype</var>,
1539+
1. If |v| does not [=implement=] {{Table}}, throw a {{LinkError}} exception.
1540+
1. Let |tableaddr| be |v|.\[[Table]].
1541+
1. Let |externtable| be the [=external value=] [=external value|table=] |tableaddr|.
1542+
1. [=list/Append=] |externtable| to |imports|.
14871543
1. [=Instantiate the core of a WebAssembly module=] |module| with |imports|, and let |instance| be the result.
1488-
1. For each |name| in the [=export name list=] of |record|,
1489-
1. Perform ! |record|.\[[Environment]].InitializeBinding(|name|, ! Get(|instance|.\[[Exports]], |name|)).
1544+
1. Set |record|.\[[Instance]] to |instance|.
1545+
1. [=list/iterate|For each=] (|name|, |externtype|) of [=module_exports=](|module|),
1546+
1. If |externtype| is of the form [=global=] |mut| |globaltype|,
1547+
1. Assert: |externval| is of the form [=external value|global=] |globaladdr|.
1548+
1. Let [=external value|global=] |globaladdr| be |externval|.
1549+
1. Let |global_value| be [=global_read=](|store|, |globaladdr|).
1550+
1. If |globaltype| is not [=v128=],
1551+
1. Note: The condition above leaves unsupported JS values as uninitialized in TDZ and therefore as a reference error on
1552+
access. When integrating with shared globals, they may be excluded here similarly to v128 above.
1553+
1. Perform [=!=] |record|.\[[Environment]].InitializeBinding(|name|, [=ToJSValue=](|global_value|)).
1554+
1. If |mut| is [=var=], then associate all future mutations of |globaladdr| with the ECMA-262 binding record for |name| in
1555+
|record|.\[[Environment]], such that |record|.\[[Environment]].GetBindingValue(|resolution|.\[[BindingName]], true)
1556+
always returns [=ToJSValue=]([=global_read=](|store|, |globaladdr|)) for the current [=surrounding agent=]'s
1557+
[=associated store=] |store|.
1558+
1. Otherwise,
1559+
1. Perform ! |record|.\[[Environment]].InitializeBinding(|name|, ! Get(|instance|.\[[Exports]], |name|)).
14901560

1491-
Note: exported bindings are left uninitialized, i.e., in TDZ.
1561+
Note: The linking semantics here for Wasm to Wasm modules are identical to the WebAssembly JS API semantics as if passing the
1562+
the exports object as the imports object in instantiation. When linking Wasm module imports to JS module exports, the JS API semantics
1563+
are exactly followed as well. It is only in the case of importing Wasm from JS that WebAssembly.Global unwrapping is observable on the
1564+
WebAssembly Module Record Environment Record.
14921565

14931566
</div>
14941567

@@ -1500,6 +1573,8 @@ WebAssembly Module Records have the following methods:
15001573
1. For each (|moduleName|, <var ignore>name</var>, <var ignore>type</var>) in [=module_imports=](|specifier|.\[[Module]]),
15011574
1. [=set/Append=] |moduleName| to |requestedModules|.
15021575
1. Let |moduleRecord| be {
1576+
<!-- WebAssembly Module Records -->
1577+
\[[Instance]]: ~empty~,
15031578
<!-- Abstract Module Records -->
15041579
\[[Realm]]: |realm|,
15051580
\[[Environment]]: ~empty~,

proposals/esm-integration/EXAMPLES.md

+13-4
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,15 @@ export const count = new WebAssembly.Global({
8181
}, 42);
8282
```
8383

84+
Note that counter.js could equivalently be written:
85+
86+
```js
87+
// counter.js
88+
export let count = 42;
89+
```
90+
91+
Which would still be picked up as a global import value, although changes to the JS value would not affect the WebAssembly global value (live bindings are only supported for Wasm module exports).
92+
8493
##### External type imports
8594

8695
```wasm
@@ -109,7 +118,7 @@ for (let index = 0; index < length; index++)
109118

110119
| export type | imported value |
111120
|-------------|---------------------------|
112-
| global | `WebAssembly.Global` object |
121+
| global | The corresponding JS value for the global |
113122
| memory | `WebAssembly.Memory` object |
114123
| table | `WebAssembly.Table` object |
115124
| function | WebAssembly exported function |
@@ -123,16 +132,16 @@ Wasm bindings cannot be reassigned as it can in JS, so the exported value will n
123132
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.
124133
1. JS module is evaluated. All values are available.
125134

126-
Currently, the value of the export for something like `WebAssembly.Global` would be accessed using the `.value` property on the JS object. However, when host bindings are in place, these could be annotated with a host binding that turns it into a real live binding that points directly to the value's address.
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.
127136

128137
#### Example
129138

130139
```js
131140
// main.js
132141
import {count, increment} from "./counter.wasm";
133-
console.log(count.value); // logs 5
142+
console.log(count); // logs 5
134143
increment();
135-
console.log(count.value); // logs 6
144+
console.log(count); // logs 6
136145
```
137146
```wasm
138147
;; counter.wat --> counter.wasm

proposals/esm-integration/README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -199,5 +199,4 @@ If custom compilation options are needed or if custom streams need to be provide
199199

200200
### Where is the specification for this proposal?
201201

202-
If you want to dig into the details, see [the updated WebAssembly JS API](https://webassembly.github.io/esm-integration/js-api/index.html#esm-integration) and [the proposed HTML integration PR](https://github.com/whatwg/html/pull/4372).
203-
202+
If you want to dig into the details, see [the updated WebAssembly JS API](https://webassembly.github.io/esm-integration/js-api/index.html#esm-integration) and [th HTML integration PR](https://github.com/whatwg/html/pull/10380).

0 commit comments

Comments
 (0)