profile-wasm-host.mjs raw
1 // profile-wasm-host.mjs — Worker bootstrap for profile.wasm.
2 import {
3 makeCoreHelpers, makeWasi, makeCommonBridge, computeBuildHash,
4 } from './bridge-common.mjs';
5
6 let mem, xp;
7 const _sabTable = new Map(), _sabSeqRef = { value: 0 };
8 const _spawnedWorkerChans = new Map();
9 const _wasmUrlRef = { value: null }, _buildHashRef = { value: null };
10 const h = makeCoreHelpers(() => mem, () => xp);
11 const { readStr, writeStr, writeI32, readBytes, writeBytes, cb0, cbs, cbb, cbdata } = h;
12
13 let _workerMsgCBID = 0;
14 let _consumerMsgCBID = 0;
15 const _pending = []; // messages that arrived before WASM was ready
16
17 function jsonEsc(s) {
18 return String(s).replace(/\\/g,'\\\\').replace(/"/g,'\\"').replace(/\n/g,'\\n').replace(/\r/g,'\\r').replace(/\t/g,'\\t');
19 }
20
21 const bridge = {
22 profile_worker_on_message(cbID) { _workerMsgCBID = cbID; },
23 profile_worker_post(ptr, len) { self.postMessage(readStr(ptr, len)); },
24 profile_worker_set_timeout(ms, cbID) { return setTimeout(() => cb0(cbID), ms); },
25 profile_worker_clear_timeout(h) { clearTimeout(h); },
26 profile_worker_now_seconds() { return BigInt(Math.floor(Date.now() / 1000)); },
27 profile_send(ptr, len) { self.postMessage({ type: 'profile-send', msg: readStr(ptr, len) }); },
28 profile_on_message(cbID) { _consumerMsgCBID = cbID; },
29 ...makeCommonBridge(h, _sabTable, _sabSeqRef, _wasmUrlRef, _buildHashRef, _spawnedWorkerChans),
30 };
31
32 const wasi = makeWasi(() => mem);
33
34 self.addEventListener('unhandledrejection', function(ev) {
35 self.postMessage('["__WASM_FATAL","unhandledrejection: ' + jsonEsc(String(ev.reason)) + '"]');
36 });
37
38 self.onmessage = async function(e) {
39 const d = e.data;
40 if (d && d.type === 'init' && d.mode === 'root') {
41 try {
42 const wasmBytes = await fetch(d.wasmUrl, { cache: 'no-store' }).then(r => r.arrayBuffer());
43 _wasmUrlRef.value = d.wasmUrl;
44 _buildHashRef.value = await computeBuildHash(wasmBytes);
45 const { instance } = await WebAssembly.instantiate(wasmBytes, { bridge, wasi_snapshot_preview1: wasi });
46 mem = instance.exports.memory; xp = instance.exports;
47 xp._start();
48 // Flush messages that arrived before WASM was ready.
49 while (_pending.length > 0) {
50 const m = _pending.shift();
51 if (typeof m === 'string' && _workerMsgCBID) cbs(_workerMsgCBID, m);
52 else if (m && m.type === '__profile_msg' && _consumerMsgCBID) cbs(_consumerMsgCBID, m.msg || '');
53 }
54 self.postMessage('["__WASM_BOOTED"]');
55 } catch (err) {
56 self.postMessage('["__WASM_FATAL","boot: ' + jsonEsc(String(err)) + '"]');
57 }
58 return;
59 }
60 if (_workerMsgCBID || _consumerMsgCBID) {
61 if (typeof d === 'string' && _workerMsgCBID) cbs(_workerMsgCBID, d);
62 if (d && d.type === '__profile_msg' && _consumerMsgCBID) cbs(_consumerMsgCBID, d.msg || '');
63 } else {
64 _pending.push(d);
65 }
66 };
67