Fix JIT closure isolation, SX wire format, server diagnostics
Root cause: _env_bind_hook mirrored ALL env_bind calls (including
lambda parameter bindings) to the shared VM globals table. Factory
functions like make-page-fn that return closures capturing different
values for the same param names (default-name, prefix, suffix) would
have the last call's values overwrite all previous closures' captured
state in globals. OP_GLOBAL_GET reads globals first, so all closures
returned the last factory call's values.
Fix: only sync root-env bindings (parent=None) to VM globals. Lambda
parameter bindings stay in their local env, found via vm_closure_env
fallback in OP_GLOBAL_GET.
Also in this commit:
- OP_CLOSURE propagates parent vm_closure_env to child closures
- Remove JIT globals injection (closure vars found via env chain)
- sx_server.ml: SX-Request header → returns text/sx (aser only)
- sx_server.ml: diagnostic endpoint GET /sx/_debug/{env,eval,route}
- sx_server.ml: page helper stubs for deep page rendering
- sx_server.ml: skip client-libs/ dir (browser-only definitions)
- adapter-html.sx: unknown components → HTML comment (not error)
- sx-platform.js: .sxbc fallback loader for bytecode modules
- Delete sx_http.ml (standalone HTTP server, unused)
- Delete stale .sxbc.json files (arity=0 bug, replaced by .sxbc)
- 7 new closure isolation tests in test-closure-isolation.sx
- mcp_tree.ml: emit arity + upvalue-count in .sxbc.json output
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -270,6 +270,7 @@
|
||||
(= name "defisland") (compile-defcomp em args scope)
|
||||
(= name "quasiquote") (compile-quasiquote em (first args) scope)
|
||||
(= name "letrec") (compile-letrec em args scope tail?)
|
||||
(= name "match") (compile-match em args scope tail?)
|
||||
;; Default — function call
|
||||
:else
|
||||
(compile-call em head args scope tail?)))))))
|
||||
@@ -634,6 +635,75 @@
|
||||
(patch-i16 em end-jump (- (current-offset em) (+ end-jump 2)))))))))))
|
||||
|
||||
|
||||
|
||||
;; compile-match — compile (match expr (pattern body) ...) to bytecode.
|
||||
;; Self-contained via letrec so JIT can find the recursive helper.
|
||||
(define compile-match
|
||||
(fn (em args scope tail?)
|
||||
(compile-expr em (first args) scope false)
|
||||
(letrec
|
||||
((do-clauses (fn (clauses)
|
||||
(if (empty? clauses)
|
||||
(do (emit-op em 5)
|
||||
(let ((idx (pool-add (get em "pool") "match: no clause matched")))
|
||||
(emit-op em 1) (emit-u16 em idx)
|
||||
(emit-op em 52) (emit-u16 em (pool-add (get em "pool") "error"))
|
||||
(emit-byte em 1)))
|
||||
(let ((clause (first clauses))
|
||||
(pattern (first clause))
|
||||
(body (nth clause 1))
|
||||
(rest-clauses (rest clauses)))
|
||||
(cond
|
||||
;; Wildcard _
|
||||
(and (= (type-of pattern) "symbol") (= (symbol-name pattern) "_"))
|
||||
(do (emit-op em 5) (compile-expr em body scope tail?))
|
||||
;; Symbol binding
|
||||
(and (= (type-of pattern) "symbol")
|
||||
(not (= (symbol-name pattern) "true"))
|
||||
(not (= (symbol-name pattern) "false"))
|
||||
(not (= (symbol-name pattern) "nil")))
|
||||
(let ((var-name (symbol-name pattern))
|
||||
(inner-scope (scope-add scope var-name)))
|
||||
(emit-op em 13) (emit-byte em (scope-index inner-scope var-name))
|
||||
(compile-expr em body inner-scope tail?))
|
||||
;; Quoted symbol 'foo
|
||||
(and (list? pattern) (= (len pattern) 2)
|
||||
(= (type-of (first pattern)) "symbol")
|
||||
(= (symbol-name (first pattern)) "quote")
|
||||
(= (type-of (nth pattern 1)) "symbol"))
|
||||
(do (emit-op em 6)
|
||||
(let ((idx (pool-add (get em "pool") (make-symbol (symbol-name (nth pattern 1))))))
|
||||
(emit-op em 1) (emit-u16 em idx))
|
||||
(let ((eq-idx (pool-add (get em "pool") "=")))
|
||||
(emit-op em 52) (emit-u16 em eq-idx) (emit-byte em 2))
|
||||
(emit-op em 33)
|
||||
(let ((skip (current-offset em)))
|
||||
(emit-i16 em 0)
|
||||
(emit-op em 5) (compile-expr em body scope tail?)
|
||||
(emit-op em 32)
|
||||
(let ((end-jump (current-offset em)))
|
||||
(emit-i16 em 0)
|
||||
(patch-i16 em skip (- (current-offset em) (+ skip 2)))
|
||||
(do-clauses rest-clauses)
|
||||
(patch-i16 em end-jump (- (current-offset em) (+ end-jump 2))))))
|
||||
;; Literal (string, number, boolean, nil)
|
||||
:else
|
||||
(do (emit-op em 6)
|
||||
(compile-expr em pattern scope false)
|
||||
(let ((eq-idx (pool-add (get em "pool") "=")))
|
||||
(emit-op em 52) (emit-u16 em eq-idx) (emit-byte em 2))
|
||||
(emit-op em 33)
|
||||
(let ((skip (current-offset em)))
|
||||
(emit-i16 em 0)
|
||||
(emit-op em 5) (compile-expr em body scope tail?)
|
||||
(emit-op em 32)
|
||||
(let ((end-jump (current-offset em)))
|
||||
(emit-i16 em 0)
|
||||
(patch-i16 em skip (- (current-offset em) (+ skip 2)))
|
||||
(do-clauses rest-clauses)
|
||||
(patch-i16 em end-jump (- (current-offset em) (+ end-jump 2))))))))))))
|
||||
(do-clauses (rest args)))))
|
||||
|
||||
(define compile-thread
|
||||
(fn (em args scope tail?)
|
||||
"Compile (-> val (f1 a) (f2 b)) by desugaring to nested calls."
|
||||
|
||||
Reference in New Issue
Block a user