Fix bytecode resume mutation order: isolate VM frames in cek_call_or_suspend
When cek_call_or_suspend runs a CEK machine for a non-bytecoded Lambda (e.g. a thunk), _active_vm still pointed to the caller's VM. VmClosure calls inside the CEK (e.g. hs-wait) would merge their frames with the caller's VM via call_closure_reuse, causing the VM to skip the CEK's remaining continuation on resume — producing wrong DOM mutation order (+active, +active, -active instead of +active, -active, +active). Fix: swap _active_vm with an empty isolation VM before running the CEK, restore after. This keeps VmClosure calls on their own frame stack while preserving js_of_ocaml exception identity (Some path, not None). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -270,8 +270,18 @@ let jit_compile_comp ~name ~params ~has_children ~body ~closure globals =
|
||||
let cek_call_or_suspend vm f args =
|
||||
incr _vm_cek_count;
|
||||
let a = match args with Nil -> [] | List l -> l | _ -> [args] in
|
||||
(* Replace _active_vm with an empty isolation VM so call_closure_reuse
|
||||
inside the CEK pushes onto an empty frame stack rather than the caller's.
|
||||
Without this, a VmClosure called from within the CEK (e.g. hs-wait)
|
||||
merges frames with the caller's VM (e.g. do-repeat), and on resume
|
||||
the VM skips the CEK's remaining continuation (wrong mutation order).
|
||||
Using Some(isolation) rather than None keeps the call_closure_reuse
|
||||
"Some" path which preserves exception identity in js_of_ocaml. *)
|
||||
let saved_active = !_active_vm in
|
||||
_active_vm := Some (create vm.globals);
|
||||
let state = Sx_ref.continue_with_call f (List a) (Env (Sx_types.make_env ())) (List a) (List []) in
|
||||
let final = Sx_ref.cek_step_loop state in
|
||||
_active_vm := saved_active;
|
||||
match Sx_runtime.get_val final (String "phase") with
|
||||
| String "io-suspended" ->
|
||||
vm.pending_cek <- Some final;
|
||||
|
||||
Reference in New Issue
Block a user