From 8ab7e367d82a02982cf7e6645b9c8df426d12a8f Mon Sep 17 00:00:00 2001 From: giles Date: Thu, 2 Apr 2026 11:23:17 +0000 Subject: [PATCH] Fix reactive regression: seed all primitives unconditionally into both tables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The conditional seeding left gaps — sync_env_to_vm() would wipe entries from vm_globals that weren't also in global_env. Unconditional seeding into both tables ensures CALL_PRIM always finds native primitives. Also prevents SX definitions (stdlib.sx has-key?) from overwriting native primitives via the env_bind hook on the server. Co-Authored-By: Claude Opus 4.6 (1M context) --- hosts/ocaml/browser/sx_browser.ml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hosts/ocaml/browser/sx_browser.ml b/hosts/ocaml/browser/sx_browser.ml index 3934ed73..1c1ddb45 100644 --- a/hosts/ocaml/browser/sx_browser.ml +++ b/hosts/ocaml/browser/sx_browser.ml @@ -857,13 +857,14 @@ let () = let () = ignore (env_bind global_env "enable-jit!" (NativeFn ("enable-jit!", fun _ -> _jit_enabled := true; Nil))) -(* Seed _vm_globals with all primitives as NativeFn values. - This makes _vm_globals the single source of truth for CALL_PRIM dispatch. - OP_DEFINE and registerNative naturally override entries here. *) +(* Seed BOTH _vm_globals AND global_env with ALL primitives as NativeFn values. + Unconditional — native primitives are authoritative for CALL_PRIM dispatch. + Must be in both because sync_env_to_vm() copies global_env → _vm_globals. *) let () = Hashtbl.iter (fun name fn -> - if not (Hashtbl.mem _vm_globals name) then - Hashtbl.replace _vm_globals name (NativeFn (name, fn)) + let v = NativeFn (name, fn) in + Hashtbl.replace _vm_globals name v; + Hashtbl.replace global_env.bindings (intern name) v ) Sx_primitives.primitives (* ================================================================== *)