From 6e997e93824fba94aec61c82213d309156c1621b Mon Sep 17 00:00:00 2001 From: giles Date: Mon, 11 May 2026 20:30:32 +0000 Subject: [PATCH] =?UTF-8?q?HS:=20test=20runner=20=E2=80=94=20auto-unwrap?= =?UTF-8?q?=20shim=20for=20new=20WASM=20kernel=20ABI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Post-JIT-Phase-1 OCaml kernels return atomic values (number, string, boolean, nil) as opaque handles {_type, __sx_handle} instead of plain JS values. The 23 K.eval call sites in hs-run-filtered.js were written against the pre-rewrite ABI and expect plain values. Add a wrapper at boot that auto-unwraps via K.stringify when the result is a handle. No-op on the legacy kernel (handles don't appear, so the check falls through). Forward-compatible: when the new WASM is the default, the shim transparently restores test compatibility. Note: This unblocks future browser-WASM rollout of JIT Phase 1. A separate issue (Set-append size regression — Expected 3, got 4 on test 27) in newer architecture-branch kernel changes still blocks the WASM rollout; the test tree continues to pin the pre-merge WASM until that regression is identified and fixed. Co-Authored-By: Claude Sonnet 4.6 --- tests/hs-run-filtered.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/hs-run-filtered.js b/tests/hs-run-filtered.js index 2d5270eb..d5baf1e1 100755 --- a/tests/hs-run-filtered.js +++ b/tests/hs-run-filtered.js @@ -14,6 +14,28 @@ const SX_DIR = path.join(WASM_DIR, 'sx'); eval(fs.readFileSync(path.join(WASM_DIR, 'sx_browser.bc.js'), 'utf8')); const K = globalThis.SxKernel; +// Auto-unwrap shim: the post-JIT-Phase-1 kernel returns numbers, strings, +// booleans, and nil as opaque value handles ({_type, __sx_handle}). Tests +// expect plain JS values from K.eval like the pre-rewrite kernel did. Wrap +// once at boot rather than touching all 23 K.eval call sites. +if (K && typeof K.eval === 'function' && K.stringify) { + const _kEval = K.eval.bind(K); + K.eval = function(expr) { + const r = _kEval(expr); + if (r && typeof r === 'object' && typeof r._type === 'string') { + switch (r._type) { + case 'number': { const s = K.stringify(r); const n = Number(s); + return Number.isInteger(n) || /^-?\d+$/.test(s) ? n : (Number.isNaN(n) ? r : n); } + case 'string': return K.stringify(r); + case 'boolean': return K.stringify(r) === 'true'; + case 'nil': return null; + default: return r; // list/dict/symbol — leave as handle + } + } + return r; + }; +} + // Step limit API — exposed from OCaml kernel const STEP_LIMIT = parseInt(process.env.HS_STEP_LIMIT || '1000000');