/* Executes WebAssembly spec-test commands (generated by gen.mjs) and prints * one line per command. The webassembly-spec-* corpus programs call it; * the differential harness runs them under Node and as compiled binaries * and compares the output, so V8 decides every expected value, trap and * error. Module bytes are inline base64 literals in each program's * data.mjs, which the build translates. */ /** The spec harness's `spectest` module (the reference interpreter's JS * harness), with its memory and table. */ function spectestImports(log) { return { print() { log("print"); }, print_i32(a) { log(`print_i32 ${a}`); }, print_i64(a) { log(`print_i64 ${a}`); }, print_f32(a) { log(`print_f32 ${show(a)}`); }, print_f64(a) { log(`print_f64 ${show(a)}`); }, print_i32_f32(a, b) { log(`print_i32_f32 ${a} ${show(b)}`); }, print_f64_f64(a, b) { log(`print_f64_f64 ${show(a)} ${show(b)}`); }, global_i32: 666, global_i64: 666n, global_f32: 666.6, global_f64: 666.6, table: new WebAssembly.Table({ initial: 10, maximum: 20, element: "anyfunc" }), memory: new WebAssembly.Memory({ initial: 1, maximum: 2 }), }; } function show(v) { if (typeof v === "bigint") return `${v}n`; if (typeof v === "number") return Object.is(v, -0) ? "-0" : String(v); if (v === null) return "null"; if (v === undefined) return "undefined"; if (typeof v === "function") return "function"; if (Array.isArray(v)) return `[${v.map(show).join(",")}]`; if (typeof v === "object" && typeof v.ref === "number") return `ref ${v.ref}`; return typeof v; } const refs = new Map(); function decode(arg) { const colon = arg.indexOf(":"); const type = arg.slice(0, colon); const text = arg.slice(colon + 1); if (type === "i32") return Number(text) | 0; if (type === "i64") return BigInt.asIntN(64, BigInt(text)); if (type === "f32") { const view = new DataView(new ArrayBuffer(4)); view.setUint32(0, Number(text)); return view.getFloat32(0); } if (type === "f64") { const view = new DataView(new ArrayBuffer(8)); view.setBigUint64(0, BigInt(text)); return view.getFloat64(0); } if (text === "null") return null; const n = Number(text); let ref = refs.get(n); if (ref === undefined) { ref = { ref: n }; refs.set(n, ref); } return ref; } function failure(e) { return e instanceof Error ? `${e.name}: ${e.message}` : `threw ${show(e)}`; } export function run(modules, script) { const commands = JSON.parse(script); const lines = []; const log = (line) => { lines.push(line); }; const imports = { spectest: spectestImports(log) }; const named = new Map(); let current = null; const instantiate = (index) => new WebAssembly.Instance(new WebAssembly.Module(Buffer.from(modules[index], "base64")), imports); let file = ""; for (const c of commands) { const op = c[0]; if (op === "file") { file = c[1]; log(`# ${file}`); continue; } if (op === "register") { const instance = c.length > 2 ? named.get(c[2]) : current; if (instance) imports[c[1]] = instance.exports; continue; } const at = `${file}:${c[1]}`; if (op === "module") { try { current = instantiate(c[2]); if (c.length > 3) named.set(c[3], current); log(`${at} module`); } catch (e) { current = null; log(`${at} module ${failure(e)}`); } continue; } if (op === "instantiate") { try { instantiate(c[2]); log(`${at} instantiated`); } catch (e) { log(`${at} ${e instanceof Error ? e.name : "threw"}`); } continue; } const instance = c[2] !== 0 ? named.get(c[2]) : current; if (!instance) { log(`${at} no instance`); continue; } try { const target = instance.exports[c[3]]; if (op === "get") { log(`${at} ${show(target.value)}`); continue; } const args = c[4].map(decode); log(`${at} ${show(target(...args))}`); } catch (e) { log(`${at} ${failure(e)}`); } } console.log(lines.join("\n")); }