Fix bytecode when/do/perform: snapshot pending_cek in resume closure
Root cause: nested cek_call_or_suspend calls on the same VM (from synchronous callbacks like dom-listen firing handler immediately) overwrote pending_cek before the first resume ran. Fix: _vm_suspension_to_dict snapshots pending_cek at capture time and restores it in the resume closure before calling resume_vm. This ensures each suspension's CEK state is preserved regardless of nested overwrite. test_bytecode_repeat.js: 4/4 pass (was 3/4). Source: 6 suspensions ✓ Bytecode: 6 suspensions ✓ Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -814,12 +814,10 @@ and run vm =
|
|||||||
let resume_vm vm result =
|
let resume_vm vm result =
|
||||||
(match vm.pending_cek with
|
(match vm.pending_cek with
|
||||||
| Some cek_state ->
|
| Some cek_state ->
|
||||||
(* Resume the suspended CEK evaluation first *)
|
|
||||||
vm.pending_cek <- None;
|
vm.pending_cek <- None;
|
||||||
let final = Sx_ref.cek_resume cek_state result in
|
let final = Sx_ref.cek_resume cek_state result in
|
||||||
(match Sx_runtime.get_val final (String "phase") with
|
(match Sx_runtime.get_val final (String "phase") with
|
||||||
| String "io-suspended" ->
|
| String "io-suspended" ->
|
||||||
(* CEK suspended again — re-suspend the VM *)
|
|
||||||
vm.pending_cek <- Some final;
|
vm.pending_cek <- Some final;
|
||||||
raise (VmSuspended (Sx_runtime.get_val final (String "request"), vm))
|
raise (VmSuspended (Sx_runtime.get_val final (String "request"), vm))
|
||||||
| _ ->
|
| _ ->
|
||||||
@@ -948,12 +946,18 @@ let () = _vm_call_closure_ref := (fun cl args -> call_closure_reuse cl args)
|
|||||||
let () = _vm_suspension_to_dict := (fun exn ->
|
let () = _vm_suspension_to_dict := (fun exn ->
|
||||||
match exn with
|
match exn with
|
||||||
| VmSuspended (request, vm) ->
|
| VmSuspended (request, vm) ->
|
||||||
|
(* Snapshot pending_cek NOW — a nested cek_call_or_suspend on the same VM
|
||||||
|
may overwrite it before our resume function is called. *)
|
||||||
|
let saved_cek = vm.pending_cek in
|
||||||
let d = Hashtbl.create 3 in
|
let d = Hashtbl.create 3 in
|
||||||
Hashtbl.replace d "__vm_suspended" (Bool true);
|
Hashtbl.replace d "__vm_suspended" (Bool true);
|
||||||
Hashtbl.replace d "request" request;
|
Hashtbl.replace d "request" request;
|
||||||
Hashtbl.replace d "resume" (NativeFn ("vm-resume", fun args ->
|
Hashtbl.replace d "resume" (NativeFn ("vm-resume", fun args ->
|
||||||
match args with
|
match args with
|
||||||
| [result] ->
|
| [result] ->
|
||||||
|
(* Restore the saved pending_cek before resuming — it may have been
|
||||||
|
overwritten by a nested suspension on the same VM. *)
|
||||||
|
vm.pending_cek <- saved_cek;
|
||||||
(try resume_vm vm result
|
(try resume_vm vm result
|
||||||
with exn2 ->
|
with exn2 ->
|
||||||
match !_vm_suspension_to_dict exn2 with
|
match !_vm_suspension_to_dict exn2 with
|
||||||
|
|||||||
Reference in New Issue
Block a user