// 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}` }); } };