read-string fell through to the literal-char branch for \u and \x,
silently stripping the backslash ("A".length returned 5 instead
of 1). Added js-hex-value helper and two cond clauses that read the
hex digits via js-peek + js-hex-digit?, compute the code point, and
emit it via char-from-code. Invalid escapes fall through to the
literal-char behaviour. built-ins/String (with --restart-every 1):
65/99 → 68/99. conformance.sh: 148/148.
11 new SX primitives in sx_primitives.ml wrapping Unix.openfile/read/write/
lseek/set_nonblock: channel-open/close/read/read-line/write/flush/seek/tell/
eof?/blocking?/set-blocking!.
Tcl runtime now uses real channel ops:
- open ?-mode? returns "fileN" handle (modes r/w/a/r+/w+/a+)
- close/read/gets/puts/seek/tell/eof/flush wired through
- new fconfigure command supports -blocking 0|1
- puts dispatches to channel-write when first arg starts with "file"
- gets command registration fixed (was pointing to old stub)
eof-returns-1 coro test updated to match real Tcl semantics (eof flips
only after a read hits EOF).
Test runner timeout bumped 180s→1200s (post-merge JIT is slow).
+7 idiom tests covering write+read, gets-loop, seek/tell, eof-after-read,
append mode, seek-to-end, fconfigure-blocking. 349/349 green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Root cause investigation of WASM kernel timeout for tests 200, 207, 211:
verified the kernel's __hs_deadline check IS firing correctly with the
JS-side _testDeadline value. The tests were genuinely taking 60s+ because
the (raise msg) inside hs-null-error! propagated up through the JIT
continuation chain and triggered the slow host_error path (~34s per
comment in the test runner override).
The companion helpers hs-null-raise! and hs-empty-raise! already wrap
their raise in (guard (_e (true nil)) (raise msg)) so the exception
is swallowed before escaping. hs-null-error! was missing this guard —
it just did (raise (str ...)).
Fix: hs-null-error! now sets window._hs_null_error and uses the same
self-contained guard pattern. The error message is still recoverable
through the side channel, matching how the eval-hs-error override in
the test harness expects to find it.
Bumped hypertrace deadlines 8s→30s (modules-loaded JIT state has grown
since the original 8s budget was set).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
With 4 parallel workers contending, the 5s default timed out 85/99
built-ins/String tests. Bumping to 15s yields 65/99 (65.7%) with
real failure modes now visible instead of "85x Timeout".
The bytecode compiler emitted OP_CALL_PRIM (52) for every primitive call, even
for arithmetic and comparison hot-paths. The VM had specialized opcodes
(OP_ADD, OP_SUB, OP_EQ, etc.) defined but unused.
- lib/compiler.sx (compile-call): emit specialized 1-byte opcode when the
primitive name + arity matches one of {+, -, *, /, =, <, >, cons, not, len,
first, rest}. Falls back to CALL_PRIM otherwise. fib bytecode: 50 → 38 bytes.
- hosts/ocaml/lib/sx_compiler.ml: mirror change in the auto-generated OCaml
compiler so SXBC export from mcp_tree uses the same emission.
- hosts/ocaml/lib/sx_vm.ml: extend OP_ADD/SUB/MUL/DIV to handle Integer+Integer
(not just Number+Number). Inline OP_EQ via Sx_runtime._fast_eq. Inline
OP_LT/GT mixed-numeric comparisons. Avoids Hashtbl lookup on the fallback
path for the common integer cases that dominate tight loops.
- hosts/ocaml/bin/bench_vm.ml: VM-only benchmark — loads compiler.sx via CEK,
JIT-compiles each fn, measures Sx_vm.call_closure throughput.
Median improvements (best of 3 runs of 9-min, bench_vm.exe):
fib(22) 107.87ms → 33.13ms -69%
loop(200000) 429.64ms → 161.16ms -62%
sum-to(50000) 72.85ms → 36.74ms -50%
count-lt(20000) 28.44ms → 17.58ms -38%
count-eq(20000) 37.23ms → 15.46ms -58%
Tests: 4550/4550 OCaml passing (unchanged). Zero regressions.
Last step in the sx-improvements roadmap — all 14 steps complete.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>