Skip to content

Commit 58dbff7

Browse files
authored
Align loader API with WebAssembly API (#916)
1 parent 4f13a73 commit 58dbff7

File tree

5 files changed

+140
-31
lines changed

5 files changed

+140
-31
lines changed

lib/loader/README.md

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ const loader = require("assemblyscript/lib/loader");
1414
API
1515
---
1616

17-
* **instantiate**<`T`>(module: `WebAssembly.Module`, imports?: `WasmImports`): `ASUtil & T`<br />
18-
Instantiates an AssemblyScript module using the specified imports.
17+
* **instantiate**<`T`>(moduleOrBuffer: `WebAssembly.Module | BufferSource | Response | PromiseLike<WebAssembly.Module | BufferSource | Response>`, imports?: `WasmImports`): `Promise<ASUtil & T>`<br />
18+
Asynchronously instantiates an AssemblyScript module from anything that can be instantiated.
1919

20-
* **instantiateBuffer**<`T`>(buffer: `Uint8Array`, imports?: `WasmImports`): `ASUtil & T`<br />
21-
Instantiates an AssemblyScript module from a buffer using the specified imports.
20+
* **instantiateSync**<`T`>(moduleOrBuffer: `WebAssembly.Module | BufferSource`, imports?: `WasmImports`): `ASUtil & T`<br />
21+
Synchronously instantiates an AssemblyScript module from a WebAssembly.Module or binary buffer.
2222

23-
* **instantiateStreaming**<`T`>(response: `Response`, imports?: `WasmImports`): `Promise<ASUtil & T>`<br />
24-
Instantiates an AssemblyScript module from a response using the specified imports.
23+
* **instantiateStreaming**<`T`>(response: `Response | PromiseLike<Response>`, imports?: `WasmImports`): `Promise<ASUtil & T>`<br />
24+
Asynchronously instantiates an AssemblyScript module from a response, i.e. as obtained by `fetch`.
2525

2626
* **demangle**<`T`>(exports: `WasmExports`, baseModule?: `Object`): `T`<br />
2727
Demangles an AssemblyScript module's exports to a friendly object structure. You usually don't have to call this manually as instantiation does this implicitly.
@@ -139,18 +139,37 @@ Examples
139139

140140
### Instantiating a module
141141

142-
```js
143-
// From a module provided as a buffer, i.e. as returned by fs.readFileSync
144-
const myModule = loader.instantiateBuffer(fs.readFileSync("myModule.wasm"), myImports);
142+
The **asynchronous API** is analogous to [WebAssembly.instantiate](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiate) and [WebAssembly.instantiateStreaming](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiateStreaming)
145143

146-
// From a response object, i.e. as returned by window.fetch
144+
```js
145+
const myModule = await loader.instantiate(myModuleBuffer, myImports);
147146
const myModule = await loader.instantiateStreaming(fetch("myModule.wasm"), myImports);
148147
```
149148

149+
with `loader.instantiate` actually accepting anything that can be instantiated for convenience:
150+
151+
```js
152+
const myModule = await loader.instantiate(fs.promises.readFile("myModule.wasm"), myImports);
153+
const myModule = await loader.instantiate(fetch("myModule.wasm"), myImports);
154+
...
155+
```
156+
157+
If `WebAssembly.instantiateStreaming` is not supported by the environment a fallback is applied.
158+
159+
The **synchronous API** utilizes [new WebAssembly.Instance](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance#Constructor_Syntax) and [new WebAssembly.Module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Module#Constructor_Syntax), which is useful if the goal is to immediately re-export as a node module for example:
160+
161+
```js
162+
module.exports = loader.instantiateSync(fs.readFileSync("myModule.wasm"), myImports);
163+
```
164+
165+
Note, though, that browsers have relatively tight limits for synchronous compilation and instantiation because these block the main thread, hence it is recommended to use the asynchronous API in browsers.
166+
150167
### Usage with TypeScript definitions produced by the compiler
151168

169+
The compiler is able to emit definitions using the `-d` command line option that are compatible with modules demangled by the loader, and these can be used for proper typings in development:
170+
152171
```ts
153172
import MyModule from "myModule"; // pointing at the d.ts
154173

155-
const myModule = loader.instatiateBuffer<typeof MyModule>(fs.readFileSync("myModule.wasm"), myImports);
174+
const myModule = await loader.instatiate<typeof MyModule>(fs.promises.readFile("myModule.wasm"), myImports);
156175
```

lib/loader/index.d.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,14 @@ interface ASUtil {
6565
__collect(): void;
6666
}
6767

68-
/** Instantiates an AssemblyScript module using the specified imports. */
69-
export declare function instantiate<T extends {}>(module: WebAssembly.Module, imports?: ImportsObject): ASUtil & T;
68+
/** Asynchronously instantiates an AssemblyScript module from anything that can be instantiated. */
69+
export declare function instantiate<T extends {}>(source: WebAssembly.Module | BufferSource | Response | PromiseLike<WebAssembly.Module | BufferSource | Response>, imports?: ImportsObject): Promise<ASUtil & T>;
7070

71-
/** Instantiates an AssemblyScript module from a buffer using the specified imports. */
72-
export declare function instantiateBuffer<T extends {}>(buffer: BufferSource, imports?: ImportsObject): ASUtil & T;
71+
/** Synchronously instantiates an AssemblyScript module from a WebAssembly.Module or binary buffer. */
72+
export declare function instantiateSync<T extends {}>(source: WebAssembly.Module | BufferSource, imports?: ImportsObject): ASUtil & T;
7373

74-
/** Instantiates an AssemblyScript module from a response using the specified imports. */
75-
export declare function instantiateStreaming<T extends {}>(result: Response | PromiseLike<Response>, imports?: ImportsObject): Promise<ASUtil & T>;
74+
/** Asynchronously instantiates an AssemblyScript module from a response, i.e. as obtained by `fetch`. */
75+
export declare function instantiateStreaming<T extends {}>(source: Response | PromiseLike<Response>, imports?: ImportsObject): Promise<ASUtil & T>;
7676

7777
/** Demangles an AssemblyScript module's exports to a friendly object structure. */
7878
export declare function demangle<T extends {}>(exports: {}, baseModule?: {}): T;

lib/loader/index.js

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -280,28 +280,54 @@ function wrapFunction(fn, setargc) {
280280
return wrap;
281281
}
282282

283-
/** Instantiates an AssemblyScript module using the specified imports. */
284-
function instantiate(module, imports) {
283+
function isResponse(o) {
284+
return typeof Response !== "undefined" && o instanceof Response;
285+
}
286+
287+
/** Asynchronously instantiates an AssemblyScript module from anything that can be instantiated. */
288+
async function instantiate(source, imports) {
289+
if (isResponse(source = await source)) return instantiateStreaming(source, imports);
285290
return postInstantiate(
286291
preInstantiate(imports || (imports = {})),
287-
new WebAssembly.Instance(module, imports)
292+
await WebAssembly.instantiate(
293+
source instanceof WebAssembly.Module
294+
? source
295+
: await WebAssembly.compile(source),
296+
imports
297+
)
288298
);
289299
}
290300

291301
exports.instantiate = instantiate;
292302

293-
/** Instantiates an AssemblyScript module from a buffer using the specified imports. */
294-
function instantiateBuffer(buffer, imports) {
295-
return instantiate(new WebAssembly.Module(buffer), imports);
303+
/** Synchronously instantiates an AssemblyScript module from a WebAssembly.Module or binary buffer. */
304+
function instantiateSync(source, imports) {
305+
return postInstantiate(
306+
preInstantiate(imports || (imports = {})),
307+
new WebAssembly.Instance(
308+
source instanceof WebAssembly.Module
309+
? source
310+
: new WebAssembly.Module(source),
311+
imports
312+
)
313+
)
296314
}
297315

298-
exports.instantiateBuffer = instantiateBuffer;
299-
300-
/** Instantiates an AssemblyScript module from a response using the specified imports. */
301-
async function instantiateStreaming(response, imports) {
316+
exports.instantiateSync = instantiateSync;
317+
318+
/** Asynchronously instantiates an AssemblyScript module from a response, i.e. as obtained by `fetch`. */
319+
async function instantiateStreaming(source, imports) {
320+
if (!WebAssembly.instantiateStreaming) {
321+
return instantiate(
322+
isResponse(source = await source)
323+
? source.arrayBuffer()
324+
: source,
325+
imports
326+
);
327+
}
302328
return postInstantiate(
303329
preInstantiate(imports || (imports = {})),
304-
(await WebAssembly.instantiateStreaming(response, imports)).instance
330+
(await WebAssembly.instantiateStreaming(source, imports)).instance
305331
);
306332
}
307333

lib/loader/tests/index.html

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,47 @@
11
<script>var exports = {};</script>
22
<script src="../index.js"></script>
33
<script>
4+
function assert(c) {
5+
if (!c) throw Error("assertion failed");
6+
}
47
(async () => {
5-
var module = await exports.instantiateStreaming(fetch("./build/untouched.wasm"));
6-
console.log(module);
8+
var module;
9+
10+
module = await exports.instantiate(fetch("./build/untouched.wasm"));
11+
assert(module.memory);
12+
13+
module = await exports.instantiate(await fetch("./build/untouched.wasm"));
14+
assert(module.memory);
15+
16+
module = await exports.instantiate((await fetch("./build/untouched.wasm")).arrayBuffer());
17+
assert(module.memory);
18+
19+
module = await exports.instantiate(await (await fetch("./build/untouched.wasm")).arrayBuffer());
20+
assert(module.memory);
21+
22+
module = await exports.instantiateStreaming(fetch("./build/untouched.wasm"));
23+
assert(module.memory);
24+
25+
module = await exports.instantiateStreaming(await fetch("./build/untouched.wasm"));
26+
assert(module.memory);
27+
28+
var instantiateStreaming = WebAssembly.instantiateStreaming;
29+
delete WebAssembly.instantiateStreaming;
30+
31+
module = await exports.instantiate(fetch("./build/untouched.wasm"));
32+
assert(module.memory);
33+
34+
module = await exports.instantiate(await fetch("./build/untouched.wasm"));
35+
assert(module.memory);
36+
37+
module = await exports.instantiateStreaming(fetch("./build/untouched.wasm"));
38+
assert(module.memory);
39+
40+
module = await exports.instantiateStreaming(await fetch("./build/untouched.wasm"));
41+
assert(module.memory);
42+
43+
WebAssembly.instantiateStreaming = instantiateStreaming;
44+
45+
console.log("ok");
746
})();
847
</script>

lib/loader/tests/index.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ var inspect = require("util").inspect;
44

55
var loader = require("..");
66
var buffer = fs.readFileSync(__dirname + "/build/untouched.wasm");
7-
var module = loader.instantiateBuffer(buffer, {});
7+
var module = loader.instantiateSync(buffer, {});
88

99
console.log(inspect(module, true, 100, true));
1010

@@ -173,3 +173,28 @@ module.dotrace(42);
173173
assert.deepEqual(view, new Float32Array([3, 2, 1]));
174174
module.__release(ptr);
175175
}
176+
177+
// should be able to instantiate from a buffer
178+
(async () => {
179+
const module = await loader.instantiate(fs.readFileSync(__dirname + "/build/untouched.wasm"), {});
180+
assert(module.memory);
181+
})();
182+
183+
// should be able to instantiate from a wasm module
184+
(async () => {
185+
const wasmModule = new WebAssembly.Module(fs.readFileSync(__dirname + "/build/untouched.wasm"));
186+
const module = await loader.instantiate(wasmModule, {});
187+
assert(module.memory);
188+
})();
189+
190+
// should be able to instantiate from a promise yielding a buffer
191+
(async () => {
192+
const module = await loader.instantiate(fs.promises.readFile(__dirname + "/build/untouched.wasm"), {});
193+
assert(module.memory);
194+
})();
195+
196+
// should be able to mimic instantiateStreaming under node (for now)
197+
(async () => {
198+
const module = await loader.instantiateStreaming(fs.promises.readFile(__dirname + "/build/untouched.wasm"), {});
199+
assert(module.memory);
200+
})();

0 commit comments

Comments
 (0)