VM adapter: compile works, env isolation needed

adapter-sx.sx compiles to 25 code objects (4044 bytes bytecode).
vm-load-module loads it. But replacing Lambda values in env.bindings
with NativeFn wrappers breaks the CEK machine for non-aser functions.

Root cause: shared env.bindings between CEK and VM. The CEK needs
Lambda values (for closure merging). The VM needs NativeFn wrappers.
Both can't coexist in the same env.

Fix needed: VM adapter gets its own globals table (with compiled
closures). The aser-slot command routes directly to the VM with
its own globals, not through the CEK with shared env.

Disabled vm-load-module. Pages render correctly via CEK.

Also: OP_CALL_PRIM now logs primitive name + argc in error messages
for easier debugging.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 21:36:38 +00:00
parent 0ce23521b7
commit df256b5607
2 changed files with 22 additions and 14 deletions

View File

@@ -233,9 +233,13 @@ let rec run vm =
let name = match consts.(idx) with String s -> s | _ -> "" in
let args = List.init argc (fun _ -> pop vm) |> List.rev in
let result =
(match Sx_primitives.get_primitive name with
| NativeFn (_, fn) -> fn args
| _ -> Nil)
try
(match Sx_primitives.get_primitive name with
| NativeFn (_, fn) -> fn args
| _ -> Nil)
with Eval_error msg ->
raise (Eval_error (Printf.sprintf "%s (in CALL_PRIM \"%s\" with %d args)"
msg name argc))
in
push vm result;
run vm
@@ -316,14 +320,15 @@ and code_from_value v =
{ arity; locals = arity + 16; bytecode = bc_list; constants }
| _ -> { arity = 0; locals = 16; bytecode = [||]; constants = [||] }
(** Execute a closure with arguments — creates a new VM frame.
The closure carries its upvalue cells for captured variables. *)
(** Execute a closure with arguments.
If called from within a VM (via NativeFn wrapper from for-each/map),
the upvalue cells already contain the captured values — no parent
frame needed. The fresh VM is fine because upvalues are heap-allocated
cells, not stack references. *)
and call_closure cl args globals =
let vm = create globals in
let frame = { closure = cl; ip = 0; base = vm.sp; local_cells = Hashtbl.create 4 } in
(* Push args as locals *)
List.iter (fun a -> push vm a) args;
(* Pad remaining locals with nil *)
for _ = List.length args to cl.code.locals - 1 do push vm Nil done;
vm.frames <- [frame];
run vm;