Skip to content

Commit c7e93c4

Browse files
committed
add initial JS API spec (WebAssembly#659)
* Add basic JS API * Expand JS in title; link MVP; Unicode mdash; deliminted * Change 'name' to an index/number * Reject, not throw, on failure in Wasm.compile * Loosen wording to allow racy copy of SAB in Wasm.compile * Mention streams as a future extension of Wasm.compile * Change TypedArray to BufferSource * s/receiver/this value/ * Fill in missing builtin-function internal slots * Simplify FutureFeatures.md sentence, remove typo * s/Wasm/WASM/ * Switch to WASM.Instance constructor and Module Namespace exports object * Add note about Wasm.instantiateModule * s/WASM/WebAssembly/
1 parent fa0118f commit c7e93c4

File tree

4 files changed

+309
-17
lines changed

4 files changed

+309
-17
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
*~
22
*#
33
.#*
4+
.*.swp
45
out

FutureFeatures.md

+23
Original file line numberDiff line numberDiff line change
@@ -402,3 +402,26 @@ separate WebAssembly [instances](Modules.md). Another use case is allowing, on
402402
the Web platform, importing a JS `ArrayBuffer` as a linear memory. This would
403403
allow highly efficient, specialized code to be generated for accessing the
404404
`ArrayBuffer`.
405+
406+
## Streaming Compilation
407+
408+
The WebAssembly binary format is designed to allow streaming decoding,
409+
validation and compilation. In the MVP, however, the only way to compile
410+
WebAssembly in a browser is through [JS API](JS.md) functions which
411+
require all code to be available in an `ArrayBuffer` before compilation
412+
can begin.
413+
414+
There are two future features that would allow streaming compilation
415+
of WebAssembly in browsers:
416+
* [ES6 Module integration](Modules.md#integration-with-es6-modules) would allow
417+
the browser's network layer to feed a stream directly into the engine.
418+
* The asynchronous [`WebAssembly.compile`](JS.md#wasmcompile) function could be
419+
extended to accept a readable stream (as defined by the
420+
[Streams API](https://streams.spec.whatwg.org))
421+
which would allow the engine to compile the stream as chunks became available,
422+
fulfilling the promise when the stream was complete. Readable streams
423+
can come from not just the network (via [`fetch`](http://fetch.spec.whatwg.org/)),
424+
but also JS stream writers and likely other future Web APIs. Thus, this feature
425+
would enable Web apps to perform their own (["layer 1"](BinaryEncoding.md))
426+
custom compression (on top of the spec-defined binary format, under generic
427+
HTTP `Content-Encoding` compression).

JS.md

+278
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
# JavaScript API
2+
3+
In the [MVP](MVP.md), the only way to access WebAssembly on the Web is through
4+
an explicit JS API which is defined below. (In the future, WebAssembly may also
5+
be loaded and run directly from an HTML `<script type='module'>` tag—and
6+
any other Web API that loads ES6 modules via URL—as part of
7+
[ES6 Module integration](Modules.md#integration-with-es6-modules).)
8+
9+
*Note: current experimental WebAssembly implementations expose a single
10+
all-in-one function `Wasm.instantiateModule(bytes, imports)` which is used
11+
by the current [demo](http://webassembly.github.io/demo). This function is
12+
basically equivalent to
13+
`new WebAssembly.Instance(new WebAssembly.Module(bytes), imports)`
14+
as defined below and will be removed at some point in the future.*
15+
16+
## The `WebAssembly` object
17+
18+
The `WebAssembly` object is the initial value of the `WebAssembly` property of
19+
the global object. Like the `Math` and `JSON` objects, the `WebAssembly` object
20+
is a plain JS object (not a constructor or function) that acts like a namespace
21+
and has the following properties:
22+
23+
### Constructor Properties of the `WebAssembly` object
24+
25+
The following intrinsic objects are added:
26+
27+
* `WebAssembly.Module` : the [`WebAssembly.Module` constructor](#wasmmodule-constructor)
28+
* `WebAssembly.Instance` : the [`WebAssembly.Instance` constructor](#wasminstance-constructor)
29+
* `WebAssembly.CompileError` : a [NativeError](http://tc39.github.io/ecma262/#sec-nativeerror-object-structure)
30+
which indicates an error during WebAssembly decoding or validation
31+
* `WebAssembly.RuntimeError` : a [NativeError](http://tc39.github.io/ecma262/#sec-nativeerror-object-structure)
32+
which indicates an error while running WebAssembly code
33+
34+
### Function Properties of the `WebAssembly` object
35+
36+
#### `WebAssembly.compile`
37+
38+
The `compile` function has the signature:
39+
```
40+
Promise<WebAssembly.Module> compile(BufferSource bytes)
41+
```
42+
If the given `bytes` argument is not a
43+
[`BufferSource`](https://heycam.github.io/webidl/#common-BufferSource),
44+
the returned `Promise` is [rejected](http://tc39.github.io/ecma262/#sec-rejectpromise)
45+
with a `TypeError`.
46+
47+
Otherwise, this function starts an asychronous task to compile a `WebAssembly.Module`
48+
as described in the [`WebAssembly.Module` constructor](#wasmmodule-constructor).
49+
On success, the `Promise` is [fulfilled](http://tc39.github.io/ecma262/#sec-fulfillpromise)
50+
with the resulting `WebAssembly.Module` instance. On failure, the `Promise` is
51+
[rejected](http://tc39.github.io/ecma262/#sec-rejectpromise) with a
52+
`WebAssembly.CompileError`.
53+
54+
The asynchronous compilation is logically performed on a copy of the state of
55+
the given `BufferSource` captured during the call to `compile`; subsequent mutations
56+
of the `BufferSource` after `compile` return do not affect ongoing compilations.
57+
58+
In the [future](FutureFeatures.md#streaming-compilation), this function can be
59+
extended to accept a [stream](https://streams.spec.whatwg.org), thereby enabling
60+
asynchronous, background, streaming compilation.
61+
62+
## `WebAssembly.Module` Objects
63+
64+
A `WebAssembly.Module` object represents the stateless result of compiling a
65+
WebAssembly binary-format module and contains one internal slot:
66+
* [[Module]] : an [`Ast.module`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/ast.ml#L208)
67+
which is the spec definition of a validated module AST
68+
69+
### `WebAssembly.Module` Constructor
70+
71+
The `WebAssembly.Module` constructor has the signature:
72+
```
73+
new Module(BufferSource bytes)
74+
```
75+
If the NewTarget is `undefined`, a `TypeError` exception is thrown (i.e., this
76+
constructor cannot be called as a function without `new`).
77+
78+
If the given `bytes` argument is not a
79+
[`BufferSource`](https://heycam.github.io/webidl/#common-BufferSource),
80+
a `TypeError` exception is thrown.
81+
82+
Otherwise, this function performs synchronous compilation of the `BufferSource`:
83+
* The byte range delimited by the `BufferSource` is first logically decoded into
84+
an AST according to [BinaryEncoding.md](BinaryEncoding.md) and then validated
85+
according to the rules in [spec/check.ml](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/check.ml#L325).
86+
* The spec `string` values inside `Ast.module` are decoded as UTF8 as described in
87+
[Web.md](Web.md#function-names).
88+
* On success, a new `WebAssembly.Module` instance is returned with [[Module]] set to
89+
the validated `Ast.module`.
90+
* On failure, a new `WebAssembly.CompileError` is thrown.
91+
92+
### Structured Clone of a `WebAssembly.Module`
93+
94+
A `WebAssembly.Module` is a
95+
[cloneable object](https://html.spec.whatwg.org/multipage/infrastructure.html#cloneable-objects)
96+
which means it can be cloned between windows/workers and also
97+
stored/retrieved into/from an [IDBObjectStore](https://w3c.github.io/IndexedDB/#object-store).
98+
The semantics of a structured clone is as-if the binary source, from which the
99+
`WebAssembly.Module` was compiled, were cloned and recompiled into the target realm.
100+
Engines should attempt to share/reuse internal compiled code when performing
101+
a structured clone although, in corner cases like CPU upgrade or browser
102+
update, this may not be possible and full recompilation may be necessary.
103+
104+
Given the above engine optimizations, structured cloning provides developers
105+
explicit control over both compiled-code caching and cross-window/worker code
106+
sharing.
107+
108+
## `WebAssembly.Instance` Objects
109+
110+
A `WebAssembly.Instance` object represents the instantiation of a
111+
`WebAssembly.Module` into a
112+
[realm](http://tc39.github.io/ecma262/#sec-code-realms) and has one
113+
internal slot:
114+
* [[Instance]] : an [`Eval.instance`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.ml#L15)
115+
which is the WebAssembly spec definition of an instance
116+
117+
as well as one plain data property (configurable, writable, enumerable)
118+
added by the constructor:
119+
* exports : a [Module Namespace Object](http://tc39.github.io/ecma262/#sec-module-namespace-objects)
120+
121+
### `WebAssembly.Instance` Constructor
122+
123+
The `WebAssembly.Instance` constructor has the signature:
124+
```
125+
new Instance(moduleObject [, importObject])
126+
```
127+
If the NewTarget is `undefined`, a `TypeError` exception is thrown (i.e., this
128+
constructor cannot be called as a function without `new`).
129+
130+
If `moduleObject` is not a `WebAssembly.Module` instance, a `TypeError` is thrown.
131+
132+
Let `module` be the [`Ast.module`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/ast.ml#L211)
133+
`moduleObject.[[Module]]`.
134+
135+
If the `importObject` parameter is not `undefined` and `Type(importObject)` is
136+
not Object, a `TypeError` is thrown. If the list of
137+
[`module.imports`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/ast.ml#L215)
138+
is not empty and `Type(importObject)` is not Object, a `TypeError` is thrown.
139+
140+
Let `imports` by an initially-empty list of JS functions.
141+
142+
For each [`import`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L135)
143+
`i` in `module.imports`:
144+
* Let `v` be the resultant value of performing
145+
[`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`importObject`, [`i.module_name`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L139)).
146+
* If [`i.func_name`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L140)
147+
is not the empty string:
148+
* If `Type(v)` is not Object, throw a `TypeError`.
149+
* Let `v` instead be the value of performing [`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`v`, `i.func_name`)
150+
* If `IsCallable(v)` is `false`, throw a `TypeError`.
151+
* Append `v` to `imports`.
152+
153+
Let `instance` be the result of evaluating
154+
[`Eval.init`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.ml#L314)
155+
with arguments `module` and `imports`.
156+
Note: this synchronously executes the [`start`](Modules.md#module-start-function)
157+
function, if present.
158+
159+
Let `exports` be an initially-empty list of (string, JS function) pairs.
160+
Let `exportedFunctions` be an initially-empty map from function indices (integers) to
161+
JS functions.
162+
163+
For each [exported function](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L128)
164+
`f` in `module.exports`:
165+
* Let `index` be the exported function index of `f`.
166+
* If `index` is not already present in `exportedFunctions`, add a mapping
167+
from `index` to the result of creating a new
168+
[Exported Function](#exported-function-exotic-objects) Exotic Object (given `instance` and `index`).
169+
* Append the pair (`f.name`, `exportedFunctions[index]`) to `exports`
170+
171+
Let `moduleRecord` be a new [WebAssembly Module Record](#webassembly-module-record) (given `exports`).
172+
173+
Let `exportStrings` be the projected list of only the first (string) components of `exports`.
174+
Let `moduleNamespace` be the result of calling
175+
[`ModuleNamespaceCreate(moduleRecord, exportStrings)`](http://tc39.github.io/ecma262/#sec-modulenamespacecreate).
176+
Set `moduleRecord.[[Namespace]]` to `moduleNamespace`.
177+
178+
Return a new `WebAssembly.Instance` object initializing `[[Instance]]` = `instance` and `exports` = `moduleNamespace`.
179+
180+
### WebAssembly Module Record
181+
182+
[Abstract Module Record](http://tc39.github.io/ecma262/#sec-abstract-module-records)
183+
is a spec-internal concept used to define ES6 modules. This abstract class currently
184+
has one concrete subclass, [Source Text Module Record](http://tc39.github.io/ecma262/#sec-source-text-module-records)
185+
which corresponds to a normal ES6 module. These interfaces are used to define the
186+
[process of loading a module on the Web](https://html.spec.whatwg.org/multipage/webappapis.html#integration-with-the-javascript-module-system).
187+
188+
When WebAssembly gets [ES6 Module integration](Modules.md#integration-with-es6-modules),
189+
a new *WebAssembly Module Record* subclass would be added which would specify
190+
the right thing to do for WebAssembly modules as part of the overall loading process.
191+
192+
Until then, the specification of [Module Namespace Exotic Objects](http://tc39.github.io/ecma262/#sec-module-namespace-exotic-objects),
193+
(used for the `WebAssembly.Instance` `exports` property) still needs to refer to *some*
194+
vestigial Module Record as part of the specification of the
195+
[\[\[Get\]\]](http://tc39.github.io/ecma262/#sec-module-namespace-exotic-objects-get-p-receiver)
196+
method.
197+
198+
More work is needed to flesh out the precise spec interaction here, but the basic
199+
idea is to create a [Module Environment Record](http://tc39.github.io/ecma262/#sec-module-environment-records)
200+
from `exports` as the [[Environment]] of a new WebAssembly Module Record.
201+
202+
## Exported Function Exotic Objects
203+
204+
Functions exported by WebAssembly modules are reflected in JS via a new kind
205+
of *Exported Function*
206+
[Exotic Object](http://tc39.github.io/ecma262/#sec-built-in-exotic-object-internal-methods-and-slots).
207+
Like [Bound Function](http://tc39.github.io/ecma262/#sec-bound-function-exotic-objects) Exotic Object,
208+
Exported Functions do not have the normal function internal slots but instead have:
209+
* [[Instance]] : the [`Eval.instance`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.ml#L15)
210+
containing the exported function
211+
* [[FunctionIndex]] : the index of the function inside the module
212+
213+
as well as the internal slots required of all builtin functions:
214+
* [[Prototype]] : [%FunctionPrototype%](http://tc39.github.io/ecma262/#sec-well-known-intrinsic-objects)
215+
* [[Extensible]] : `true`
216+
* [[Realm]] : the [current Realm Record](http://tc39.github.io/ecma262/#current-realm)
217+
* [[ScriptOrModule]] : [`GetActiveScriptOrModule`](http://tc39.github.io/ecma262/#sec-getactivescriptormodule)
218+
219+
Exported Functions also have the following data properties:
220+
* the `length` property is set to the exported function's signature's arity
221+
* the `name` is set to `index` as a Number value
222+
223+
WebAssembly Exported Functions have a `[[Call]](this, argValues)` method defined as:
224+
* Let `argTypes` be the list of value types defined by the signature of [[FunctionIndex]].
225+
* Let `args` be an empty list of coerced values.
226+
* For each value type `t` in `argTypes` and value `v` in `argValues`:
227+
* Append to `args` `v` coerced to `t` as follows:
228+
* coerce `v` to `i32` via [`ToInt32(v)`](http://tc39.github.io/ecma262/#sec-toint32)
229+
* throw a `TypeError` if `t` is `i64`
230+
* coerce `v` to `f32` by first applying [`ToNumber(v)`](http://tc39.github.io/ecma262/#sec-tonumber)
231+
and then converting the resulting IEEE754 64-bit double to a 32-bit float using `roundTiesToEven`
232+
* coerce `v` to `f64` via [`ToNumber(v)`](http://tc39.github.io/ecma262/#sec-tonumber)
233+
* Perform [`Eval.invoke`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.ml#L327)
234+
passing [[Instance]], [[FunctionIndex]], and `args`.
235+
* Coerce the result of `Eval.invoke` as follows:
236+
* if the return value is `None`, return `undefined`
237+
* interpret `i32` as a signed integer and convert that integer to a Number value
238+
* throw a `TypeError` if returning an `i64`
239+
* return `f32`/`f64` as Number values, possibly performing
240+
[canonicalization of NaNs](http://tc39.github.io/ecma262/#sec-setvalueinbuffer)
241+
242+
Exported Functions do not have a [[Construct]] method and thus it is not possible to
243+
call one with the `new` operator.
244+
245+
## Sample API Usage
246+
247+
Given `demo.was` (encoded to `demo.wasm`):
248+
```lisp
249+
(module
250+
(import $i1 "m" "import1")
251+
(import $i2 "import2" "")
252+
(func $main (call_import $i1))
253+
(start $main)
254+
(func $f (call_import $i2))
255+
(export "f" $f)
256+
)
257+
```
258+
and the following JavaScript, run in a browser:
259+
```javascript
260+
fetch('demo.wasm').then(response =>
261+
response.arrayBuffer()
262+
).then(buffer =>
263+
WebAssembly.compile(buffer)
264+
).then(module => {
265+
var importObj = {
266+
m: {import1: () => console.log("hello, ")},
267+
import2: () => console.log("world!\n")
268+
};
269+
var instance = new WebAssembly.Instance(module, importObj); // "hello, "
270+
instance.exports.f(); // "world!"
271+
});
272+
```
273+
274+
## TODO
275+
276+
* `WebAssembly.Memory`: imports, exports
277+
* `WebAssembly.Table`: imports, exports
278+
* `WebAssembly.Module` `exports`/`imports` properties (reflection)

Web.md

+7-17
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,18 @@ than if the module was JavaScript.
1414
More concretely, the following is a list of points of contact between WebAssembly
1515
and the rest of the Web platform that have been considered:
1616

17-
## The `Wasm` object
17+
## JavaScript API
1818

19-
In addition to
20-
[integration with the ES6 Modules system](Modules.md#integration-with-es6-modules),
21-
WebAssembly is exposed to the Web through a `Wasm` object whose API is designed
22-
to provide a powerful, idiomatic set of methods and properties to instantiate
23-
and introspect WebAssembly modules directly from JavaScript.
19+
A [JavaScript API](JS.md) is provided which allows JavaScript to compile
20+
WebAssembly modules, perform limited reflection on compiled modules, store
21+
and retrieve compiled modules from offline storage, instantiate compiled modules
22+
with JavaScript imports, call the exported functions of instantiated modules,
23+
alias the exported memory of instantiated modules, etc.
2424

2525
## Modules
2626

2727
WebAssembly's [modules](Modules.md) allow for natural [integration with
28-
the ES6 module system](Modules.md#integration-with-es6-modules) and allow
29-
synchronous calling to and from JavaScript.
28+
the ES6 module system](Modules.md#integration-with-es6-modules).
3029

3130
### Function Names
3231

@@ -93,15 +92,6 @@ WebAssembly's security model should depend on [CORS][] and
9392
distribution networks and to implement
9493
[dynamic linking](DynamicLinking.md).
9594

96-
## Threads
97-
98-
Once [threads are supported](PostMVP.md#threads), a WebAssembly module would
99-
be shared (including its heap) between workers via `postMessage()`.
100-
* This also has the effect of explicitly sharing code so that engines don't
101-
perform N fetches and compile N copies.
102-
* WebAssembly may later standardize a more direct way to create a thread that
103-
doesn't involve creating a new Worker.
104-
10595
## SIMD
10696

10797
Once [SIMD is supported](PostMVP.md#fixed-width-simd) WebAssembly would:

0 commit comments

Comments
 (0)