Fix isomorphic SSR: revert inline opcodes, add named let compilation, fix cookie decode

Three bugs broke island SSR rendering of the home stepper widget:

1. Inline VM opcodes (OP_ADD..OP_DEC) broke JIT-compiled functions.
   The compiler emitted single-byte opcodes for first/rest/len/= etc.
   that produced wrong results in complex recursive code (sx-parse
   returned nil, split-tag produced 1 step instead of 16). Reverted
   compiler to use CALL_PRIM for all primitives. VM opcode handlers
   kept for future use.

2. Named let (let loop ((x init)) body) had no compiler support —
   silently produced broken bytecode. Added desugaring to letrec.

3. URL-encoded cookie values not decoded server-side. Client set-cookie
   uses encodeURIComponent but Werkzeug doesn't decode cookie values.
   Added unquote() in bridge cookie injection.

Also: call-lambda used eval_expr which copies Dict values (signals),
breaking mutations through aser lambda calls. Switched to cek_call.

Also: stepper preview now includes ~cssx/tw spreads for SSR styling.

Tests: 1317 JS, 1114 OCaml, 26 integration (2 pre-existing failures)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-24 22:32:51 +00:00
parent eb4233ff36
commit 57cffb8bcc
10 changed files with 360 additions and 70 deletions

View File

@@ -432,3 +432,37 @@
(render-sx
"(do (defcomp ~page (&key x) (div x))
(case \"miss\" \"hit\" (~page :x \"found\") :else \"index\"))"))))
;; --------------------------------------------------------------------------
;; Dict mutation through lambda calls in aser body
;; --------------------------------------------------------------------------
;;
;; Regression: aser's :else branch used call-lambda which re-evaluated
;; args through eval_expr. The CEK evaluator copies Dict values during
;; evaluation (treating them as dict literals), so mutations inside the
;; lambda operated on a copy, not the original. This broke signal
;; reset!/swap! in island SSR where aser processes multi-body let forms.
(defsuite "aser-dict-mutation"
(deftest "lambda mutating dict arg in multi-body let"
(assert-equal "99"
(render-sx
"(let ((mutate! (fn (d k v) (dict-set! d k v)))
(d (dict \"x\" 1)))
(mutate! d \"x\" 99)
(get d \"x\"))")))
(deftest "signal reset! in multi-body let"
(assert-equal "99"
(render-sx
"(let ((s (signal 42)))
(reset! s 99)
(deref s))")))
(deftest "signal reset! then len of deref in multi-body let"
(assert-equal "3"
(render-sx
"(let ((s (signal (list))))
(reset! s (list 1 2 3))
(len (deref s)))"))))