WASM kernel fixes: parse, env sync, iterative CEK, click delegation
Browser kernel: - Add `parse` native fn (matches server: unwrap single, list for multiple) - Restore env==global_env guard on _env_bind_hook (let bindings must not leak to _vm_globals — caused JIT CSSX "Not callable: nil" errors) - Add _env_bind_hook call in env_set_id so set! mutations sync to VM globals - Fire _vm_global_set_hook from OP_DEFINE so VM defines sync back to CEK env CEK evaluator: - Replace recursive cek_run with iterative while loop using sx_truthy (previous attempt used strict Bool true matching, broke in wasm_of_ocaml) - Remove dead cek_run_iterative function Web modules: - Remove find-matching-route and parse-route-pattern stubs from boot-helpers.sx that shadowed real implementations from router.sx - Sync boot-helpers.sx to dist/static dirs for bytecode compilation Platform (sx-platform.js): - Set data-sx-ready attribute after boot completes (was only in boot-init which sx-platform.js doesn't call — it steps through boot manually) - Add document-level click delegation for a[sx-get] links as workaround for bytecoded bind-event not attaching per-element listeners (VM closure issue under investigation — bind-event runs but dom-add-listener calls don't result in addEventListener) Tests: - New test_kernel.js: 24 tests covering env sync, parse, route matching, host FFI/preventDefault, deep recursion - New navigation test: "sx-get link fetches SX not HTML and preserves layout" (currently catches layout breakage after SPA swap — known issue) Known remaining issues: - JIT CSSX failures: closure-captured variables resolve to nil in VM bytecode - SPA content swap via execute-request breaks page layout - Bytecoded bind-event doesn't attach per-element addEventListener (root cause unknown — when listen-target guard appears to block despite element being valid) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -372,13 +372,17 @@ and sf_provide args env =
|
||||
and expand_macro mac raw_args env =
|
||||
(let local = (env_merge ((macro_closure (mac))) (env)) in (let () = ignore ((List.iter (fun pair -> ignore ((env_bind local (sx_to_string (first (pair))) (if sx_truthy ((prim_call "<" [(nth (pair) ((Number 1.0))); (len (raw_args))])) then (nth (raw_args) ((nth (pair) ((Number 1.0))))) else Nil)))) (sx_to_list (List (List.mapi (fun i p -> let i = Number (float_of_int i) in (List [p; i])) (sx_to_list (macro_params (mac)))))); Nil)) in (let () = ignore ((if sx_truthy ((macro_rest_param (mac))) then (env_bind local (sx_to_string (macro_rest_param (mac))) (prim_call "slice" [raw_args; (len ((macro_params (mac))))])) else Nil)) in (trampoline ((eval_expr ((macro_body (mac))) (local)))))))
|
||||
|
||||
(* cek-run *)
|
||||
(* cek-run — iterative to avoid OCaml/WASM stack overflow *)
|
||||
and cek_run state =
|
||||
(if sx_truthy ((cek_terminal_p (state))) then (cek_value (state)) else
|
||||
try cek_run ((cek_step (state)))
|
||||
with Eval_error msg ->
|
||||
(if !_last_error_kont = Nil then _last_error_kont := cek_kont state);
|
||||
raise (Eval_error msg))
|
||||
let s = ref state in
|
||||
(try
|
||||
while not (sx_truthy (cek_terminal_p !s)) do
|
||||
s := cek_step !s
|
||||
done;
|
||||
cek_value !s
|
||||
with Eval_error msg ->
|
||||
(if !_last_error_kont = Nil then _last_error_kont := cek_kont !s);
|
||||
raise (Eval_error msg))
|
||||
|
||||
(* cek-step *)
|
||||
and cek_step state =
|
||||
@@ -590,19 +594,6 @@ let () = trampoline_fn := (fun v ->
|
||||
(* Wire up the primitives trampoline so call_any in HO forms resolves Thunks *)
|
||||
let () = Sx_primitives._sx_trampoline_fn := !trampoline_fn
|
||||
|
||||
(* Override recursive cek_run with iterative loop.
|
||||
On error, capture the kont from the last state for comp-trace. *)
|
||||
let cek_run_iterative state =
|
||||
let s = ref state in
|
||||
(try
|
||||
while not (match cek_terminal_p !s with Bool true -> true | _ -> false) do
|
||||
s := cek_step !s
|
||||
done;
|
||||
cek_value !s
|
||||
with Eval_error msg ->
|
||||
_last_error_kont := cek_kont !s;
|
||||
raise (Eval_error msg))
|
||||
|
||||
(* Collect component trace from a kont value *)
|
||||
let collect_comp_trace kont =
|
||||
let trace = ref [] in
|
||||
|
||||
@@ -219,9 +219,11 @@ let rec env_get_id env id name =
|
||||
let env_get env name = env_get_id env (intern name) name
|
||||
|
||||
let rec env_set_id env id v =
|
||||
if Hashtbl.mem env.bindings id then
|
||||
(Hashtbl.replace env.bindings id v; Nil)
|
||||
else
|
||||
if Hashtbl.mem env.bindings id then begin
|
||||
Hashtbl.replace env.bindings id v;
|
||||
(match !_env_bind_hook with Some f -> f env (unintern id) v | None -> ());
|
||||
Nil
|
||||
end else
|
||||
match env.parent with
|
||||
| Some p -> env_set_id p id v
|
||||
| None -> Hashtbl.replace env.bindings id v; Nil
|
||||
|
||||
@@ -431,7 +431,9 @@ and run vm =
|
||||
let idx = read_u16 frame in
|
||||
let name = match consts.(idx) with String s -> s | _ -> "" in
|
||||
let v = peek vm in
|
||||
Hashtbl.replace vm.globals name v
|
||||
Hashtbl.replace vm.globals name v;
|
||||
(match !Sx_types._vm_global_set_hook with
|
||||
| Some f -> f name v | None -> ())
|
||||
|
||||
(* ---- Inline primitives ----
|
||||
Fast path for common types; fallback to actual primitive
|
||||
|
||||
Reference in New Issue
Block a user