diff --git a/hosts/ocaml/lib/sx_vm.ml b/hosts/ocaml/lib/sx_vm.ml index 4bc830e..d6a99a4 100644 --- a/hosts/ocaml/lib/sx_vm.ml +++ b/hosts/ocaml/lib/sx_vm.ml @@ -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; diff --git a/shared/sx/ocaml_bridge.py b/shared/sx/ocaml_bridge.py index 4e6c277..517ed11 100644 --- a/shared/sx/ocaml_bridge.py +++ b/shared/sx/ocaml_bridge.py @@ -333,13 +333,16 @@ class OcamlBridge: _logger.info("Loaded %d definitions from .sx files into OCaml kernel (%d skipped)", count, skipped) - # Compile adapter-sx.sx to bytecode and load as VM module. - # All aser functions become NativeFn VM closures in the - # kernel env. The CEK calls them as NativeFn → VM executes. - try: - await self._compile_adapter_module() - except Exception as e: - _logger.warning("VM adapter compilation skipped: %s", e) + # VM adapter compilation: compile adapter-sx.sx to bytecode, + # load as VM module so aser runs compiled. + # DISABLED: vm-load-module replaces env bindings with NativeFn + # wrappers that break when the CEK machine calls other env + # functions during page eval. Need to isolate VM execution + # from CEK env to avoid cross-contamination. + # try: + # await self._compile_adapter_module() + # except Exception as e: + # _logger.warning("VM adapter compilation skipped: %s", e) except Exception as e: _logger.error("Failed to load .sx files into OCaml kernel: %s", e) self._components_loaded = False # retry next time