|
| 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) |
0 commit comments