1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// Runs one call of a compiled module off the page's thread, so a module that never
// returns can be terminated. Same JSON ABI as emdash's in-process runner: exports memory,
// alloc and entry; entry(ptr, len) returns an i64 packing (outputPtr << 32 | outputLen).
// Failures are messages starting with their WASM_* code.
function run(compiled, entry, inputJson, limits) {
const { exports } = new WebAssembly.Instance(compiled, {});
const { memory, alloc } = exports;
if (!(memory instanceof WebAssembly.Memory) || typeof alloc !== "function" || typeof exports[entry] !== "function") {
throw new Error(`WASM_ABI_INVALID: module must export memory, alloc and ${entry}`);
}
const input = new TextEncoder().encode(inputJson);
let packed;
try {
const pointer = Number(alloc(input.byteLength));
new Uint8Array(memory.buffer, pointer, input.byteLength).set(input);
packed = exports[entry](pointer, input.byteLength);
} catch (error) {
throw new Error(`WASM_TRAP: ${error.message}`);
}
// wasmAccess capped the memory's maximum before compiling (capMemory): this is a backstop.
if (memory.buffer.byteLength > limits.memoryPages * 65536) throw new Error(`WASM_TOO_LARGE: memory exceeds ${limits.memoryPages} pages`);
if (typeof packed !== "bigint") throw new Error(`WASM_ABI_INVALID: ${entry} must return i64`);
const outputPointer = Number(BigInt.asUintN(64, packed) >> 32n);
const outputLength = Number(BigInt.asUintN(32, packed));
if (outputLength > limits.outputBytes) throw new Error(`WASM_OUTPUT_INVALID: output exceeds ${limits.outputBytes} bytes`);
if (outputPointer + outputLength > memory.buffer.byteLength) throw new Error("WASM_OUTPUT_INVALID: output points outside memory");
try {
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(new Uint8Array(memory.buffer, outputPointer, outputLength)));
} catch {
throw new Error("WASM_OUTPUT_INVALID: output is not JSON");
}
}
self.onmessage = ({ data: { compiled, entry, inputJson, limits } }) => {
try {
self.postMessage({ output: run(compiled, entry, inputJson, limits) });
} catch (error) {
self.postMessage({ error: error.message.startsWith("WASM_") ? error.message : `WASM_TRAP: ${error.message}` });
}
};