VM import suspension for browser lazy loading
Bytecode compiler now emits OP_PERFORM for (import ...) and compiles
(define-library ...) bodies. The VM stores the import request in
globals["__io_request"] and stops the run loop — no exceptions needed.
vm-execute-module returns a suspension dict, vm-resume-module continues.
Browser: sx_browser.ml detects suspension dicts from execute_module and
returns JS {suspended, op, request, resume} objects. The sx-platform.js
while loop handles cascading suspensions via handleImportSuspension.
13 modules load via .sxbc bytecode in 226ms (manifest-driven), both
islands hydrate, all handlers wired. 2650/2650 tests pass including
6 new vm-import-suspension tests.
Also: consolidated sx-platform-2.js → sx-platform.js, fixed
vm-execute-module missing code-from-value call, fixed bootstrap.py
protocol registry transpiler issues.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -243,7 +243,7 @@ let api_parse src_js =
|
||||
|
||||
(** Build a JS suspension marker for the platform to handle.
|
||||
Returns {suspended: true, op: string, request: obj, resume: fn(result)} *)
|
||||
let make_js_suspension request resume_fn =
|
||||
let _make_js_suspension request resume_fn =
|
||||
let obj = Js.Unsafe.obj [||] in
|
||||
Js.Unsafe.set obj (Js.string "suspended") (Js.Unsafe.inject (Js.bool true));
|
||||
let op = match request with
|
||||
@@ -380,44 +380,36 @@ let sync_vm_to_env () =
|
||||
end
|
||||
) _vm_globals
|
||||
|
||||
(** Recursive suspension handler: resumes VM, catches further suspensions,
|
||||
resolves imports locally when possible, otherwise returns JS suspension
|
||||
objects that the platform's while loop can process. *)
|
||||
let rec resume_with_suspensions vm result =
|
||||
try
|
||||
let v = Sx_vm.resume_vm vm result in
|
||||
(** Convert a VM suspension dict to a JS suspension object for the platform. *)
|
||||
let rec make_js_import_suspension (d : (string, value) Hashtbl.t) =
|
||||
let obj = Js.Unsafe.obj [||] in
|
||||
Js.Unsafe.set obj (Js.string "suspended") (Js.Unsafe.inject Js._true);
|
||||
Js.Unsafe.set obj (Js.string "op") (Js.Unsafe.inject (Js.string "import"));
|
||||
let request = match Hashtbl.find_opt d "request" with Some v -> v | None -> Nil in
|
||||
Js.Unsafe.set obj (Js.string "request") (value_to_js request);
|
||||
(* resume callback: clears __io_request, pushes nil, re-runs VM *)
|
||||
Js.Unsafe.set obj (Js.string "resume") (Js.wrap_callback (fun _result_js ->
|
||||
let resumed = Sx_vm_ref.resume_module (Dict d) in
|
||||
sync_vm_to_env ();
|
||||
value_to_js v
|
||||
with Sx_vm.VmSuspended (request, vm2) ->
|
||||
handle_suspension request vm2
|
||||
|
||||
and handle_suspension request vm =
|
||||
let op = match request with
|
||||
| Dict d -> (match Hashtbl.find_opt d "op" with Some (String s) -> s | _ -> "")
|
||||
| _ -> "" in
|
||||
if op = "import" then
|
||||
match handle_import_suspension request with
|
||||
| Some result ->
|
||||
(* Library already loaded — resume and handle further suspensions *)
|
||||
resume_with_suspensions vm result
|
||||
| None ->
|
||||
(* Library not loaded — return suspension to JS for async fetch *)
|
||||
Js.Unsafe.inject (make_js_suspension request (fun _result ->
|
||||
resume_with_suspensions vm Nil))
|
||||
else
|
||||
Js.Unsafe.inject (make_js_suspension request (fun result ->
|
||||
resume_with_suspensions vm result))
|
||||
match resumed with
|
||||
| Dict d2 when (match Hashtbl.find_opt d2 "suspended" with Some (Bool true) -> true | _ -> false) ->
|
||||
Js.Unsafe.inject (make_js_import_suspension d2)
|
||||
| result -> value_to_js result));
|
||||
obj
|
||||
|
||||
let api_load_module module_js =
|
||||
try
|
||||
let code_val = js_to_value module_js in
|
||||
let code = Sx_vm.code_from_value code_val in
|
||||
let _result = Sx_vm_ref.execute_module code _vm_globals in
|
||||
sync_vm_to_env ();
|
||||
Js.Unsafe.inject (Hashtbl.length _vm_globals)
|
||||
let result = Sx_vm_ref.execute_module code _vm_globals in
|
||||
match result with
|
||||
| Dict d when (match Hashtbl.find_opt d "suspended" with Some (Bool true) -> true | _ -> false) ->
|
||||
(* VM suspended on OP_PERFORM (import) — return JS suspension object *)
|
||||
Js.Unsafe.inject (make_js_import_suspension d)
|
||||
| _ ->
|
||||
sync_vm_to_env ();
|
||||
Js.Unsafe.inject (Hashtbl.length _vm_globals)
|
||||
with
|
||||
| Sx_vm.VmSuspended (request, vm) ->
|
||||
handle_suspension request vm
|
||||
| Eval_error msg -> Js.Unsafe.inject (Js.string ("Error: " ^ msg))
|
||||
| exn -> Js.Unsafe.inject (Js.string ("Error: " ^ Printexc.to_string exn))
|
||||
|
||||
@@ -628,7 +620,7 @@ let () =
|
||||
in
|
||||
let module_val = convert_code code_form in
|
||||
let code = Sx_vm.code_from_value module_val in
|
||||
let _result = Sx_vm_ref.execute_module code _vm_globals in
|
||||
let _result = Sx_vm.execute_module code _vm_globals in
|
||||
sync_vm_to_env ();
|
||||
Number (float_of_int (Hashtbl.length _vm_globals))
|
||||
| _ -> raise (Eval_error "load-sxbc: expected (sxbc version hash (code ...))"));
|
||||
|
||||
Reference in New Issue
Block a user