Recompile all 26 .sxbc with define-library wrappers + fix eval/JIT

All 26 browser modules recompiled with define-library/import forms.
Compilation works without vm-compile-adapter (JIT pre-compilation
hangs with library wrappers in some JIT paths — skipped for now,
CEK compilation is ~34s total).

Key fixes:
- eval command: import-aware loop that handles define-library/import
  locally without touching the Python bridge pipe (avoids deadlock)
- compile-modules.js: skip vm-compile-adapter, bump timeout

2621/2621 OCaml tests passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-04 00:08:00 +00:00
parent ac772ac357
commit 7b4c918773
48 changed files with 8884 additions and 5899 deletions

View File

@@ -50,6 +50,13 @@ let rec deep_equal a b =
deep_equal
(match Hashtbl.find_opt a k with Some v -> v | None -> Nil)
(match Hashtbl.find_opt b k with Some v -> v | None -> Nil)) ka
| Record a, Record b ->
a.r_type.rt_uid = b.r_type.rt_uid &&
Array.length a.r_fields = Array.length b.r_fields &&
(let eq = ref true in
for i = 0 to Array.length a.r_fields - 1 do
if not (deep_equal a.r_fields.(i) b.r_fields.(i)) then eq := false
done; !eq)
| Lambda _, Lambda _ -> a == b (* identity *)
| NativeFn _, NativeFn _ -> a == b
| _ -> false

View File

@@ -1030,9 +1030,29 @@ let rec dispatch env cmd =
(try
let exprs = Sx_parser.parse_all src in
let result = List.fold_left (fun _acc expr ->
(* Use IO-aware eval to handle import suspensions *)
(* Use import-aware eval handles define-library/import locally
but does NOT send other IO to the Python bridge (would deadlock
on stdin which carries batch commands). *)
let state = Sx_ref.make_cek_state expr (Env env) (List []) in
cek_run_with_io state
let s = ref (Sx_ref.cek_step_loop state) in
while Sx_types.sx_truthy (Sx_ref.cek_suspended_p !s) do
let request = Sx_ref.cek_io_request !s in
let op = match request with
| Dict d -> (match Hashtbl.find_opt d "op" with Some (String o) -> o | _ -> "")
| _ -> "" in
let response = if op = "import" then begin
let lib_spec = Sx_runtime.get_val request (String "library") in
let key = Sx_ref.library_name_key lib_spec in
if Sx_types.sx_truthy (Sx_ref.library_loaded_p key) then Nil
else begin
(match resolve_library_path lib_spec with
| Some path -> load_library_file path | None -> ());
Nil
end
end else Nil (* non-import IO: resume with nil *) in
s := Sx_ref.cek_resume !s response
done;
Sx_ref.cek_value !s
) Nil exprs in
(* Use ok-raw with natural list serialization — no (list ...) wrapping.
This preserves the SX structure for Python to parse back. *)

View File

@@ -56,8 +56,10 @@ let script = '';
// Load compiler
script += `(epoch ${epoch++})\n(load "lib/compiler.sx")\n`;
// JIT pre-compile the compiler
script += `(epoch ${epoch++})\n(vm-compile-adapter)\n`;
// JIT pre-compile the compiler (skipped: vm-compile-adapter hangs with
// define-library wrappers in some lambda JIT paths. Compilation still
// works via CEK — just ~2x slower per file.)
// script += `(epoch ${epoch++})\n(vm-compile-adapter)\n`;
// Load all modules into env
for (const file of FILES) {

View File

@@ -200,6 +200,14 @@ let () =
(match Hashtbl.find_opt a "__host_handle", Hashtbl.find_opt b "__host_handle" with
| Some (Number ha), Some (Number hb) -> ha = hb
| _ -> false)
(* Records: same type + structurally equal fields *)
| Record a, Record b ->
a.r_type.rt_uid = b.r_type.rt_uid &&
Array.length a.r_fields = Array.length b.r_fields &&
(let eq = ref true in
for i = 0 to Array.length a.r_fields - 1 do
if not (safe_eq a.r_fields.(i) b.r_fields.(i)) then eq := false
done; !eq)
(* Lambda/Component/Island/Signal/NativeFn: physical only *)
| _ -> false
in
@@ -723,6 +731,7 @@ let () =
| [Island i] ->
String (Printf.sprintf "~%s" i.i_name)
| [Lambda _] -> String "<lambda>"
| [Record r] -> String (Printf.sprintf "#<%s>" r.r_type.rt_name)
| [a] -> String (inspect a) (* used for dedup keys in compiler *)
| _ -> raise (Eval_error "serialize: 1 arg"));
register "make-symbol" (fun args ->
@@ -912,6 +921,36 @@ let () =
match args with [Lambda _] -> Bool true | _ -> Bool false);
register "island?" (fun args ->
match args with [Island _] -> Bool true | _ -> Bool false);
(* R7RS records *)
register "record?" (fun args ->
match args with [v] -> record_p v | _ -> Bool false);
register "make-rtd" (fun args ->
match args with [name; fields; ctor_params] -> make_rtd name fields ctor_params
| _ -> raise (Eval_error "make-rtd: expected (name fields ctor-params)"));
register "make-record" (fun args ->
match args with [uid; arg_list] -> make_record uid arg_list
| _ -> raise (Eval_error "make-record: expected (uid args-list)"));
register "record-ref" (fun args ->
match args with [v; idx] -> record_ref v idx
| _ -> raise (Eval_error "record-ref: expected (record index)"));
register "record-set!" (fun args ->
match args with [v; idx; nv] -> record_set_b v idx nv
| _ -> raise (Eval_error "record-set!: expected (record index value)"));
register "record-type?" (fun args ->
match args with [v; uid] -> record_type_p v uid | _ -> Bool false);
register "make-record-constructor" (fun args ->
match args with [uid] -> make_record_constructor uid
| _ -> raise (Eval_error "make-record-constructor: expected (uid)"));
register "make-record-predicate" (fun args ->
match args with [uid] -> make_record_predicate uid
| _ -> raise (Eval_error "make-record-predicate: expected (uid)"));
register "make-record-accessor" (fun args ->
match args with [idx] -> make_record_accessor idx
| _ -> raise (Eval_error "make-record-accessor: expected (index)"));
register "make-record-mutator" (fun args ->
match args with [idx] -> make_record_mutator idx
| _ -> raise (Eval_error "make-record-mutator: expected (index)"));
register "is-else-clause?" (fun args ->
match args with
| [Keyword "else"] -> Bool true

File diff suppressed because one or more lines are too long

View File

@@ -67,6 +67,7 @@ and value =
| CekState of cek_state (** Optimized CEK machine state — avoids Dict allocation. *)
| CekFrame of cek_frame (** Optimized CEK continuation frame. *)
| VmClosure of vm_closure (** VM-compiled closure — callable within the VM without allocating a new VM. *)
| Record of record (** R7RS record — opaque, generative, field-indexed. *)
(** CEK machine state — record instead of Dict for performance.
5 fields × 55K steps/sec = 275K Hashtbl allocations/sec eliminated. *)
@@ -139,6 +140,21 @@ and signal = {
mutable s_deps : signal list;
}
(** R7RS record type descriptor — one per [define-record-type] call.
Stored in [rtd_table]; closures capture only the integer uid. *)
and record_type = {
rt_name : string; (** e.g., "point" *)
rt_uid : int; (** unique identity — generative *)
rt_fields : string array; (** field names in declaration order *)
rt_ctor_map : int array; (** ctor_map[i] = field index for ctor param i *)
}
(** R7RS record instance — opaque, accessed only through generated functions. *)
and record = {
r_type : record_type;
r_fields : value array; (** mutable via Array.set for record-set! *)
}
(** {1 Bytecode VM types}
Defined here (not in sx_vm.ml) because [vm_code.constants] references
@@ -180,6 +196,12 @@ exception Eval_error of string
exception Parse_error of string
(** {1 Record type descriptor table} *)
let rtd_table : (int, record_type) Hashtbl.t = Hashtbl.create 16
let rtd_counter = ref 0
(** {1 Environment operations} *)
let make_env () =
@@ -347,6 +369,7 @@ let type_of = function
| CekState _ -> "dict" (* CEK state behaves as a dict for type checks *)
| CekFrame _ -> "dict"
| VmClosure _ -> "function"
| Record r -> r.r_type.rt_name
let is_nil = function Nil -> true | _ -> false
let is_lambda = function Lambda _ -> true | _ -> false
@@ -359,6 +382,8 @@ let is_signal = function
| Dict d -> Hashtbl.mem d "__signal"
| _ -> false
let is_record = function Record _ -> true | _ -> false
let is_callable = function
| Lambda _ | NativeFn _ | Continuation (_, _) | CallccContinuation _ | VmClosure _ -> true
| _ -> false
@@ -470,6 +495,130 @@ let thunk_env = function
| v -> raise (Eval_error ("Expected thunk, got " ^ type_of v))
(** {1 Record operations} *)
let val_to_int = function
| Number n -> int_of_float n
| v -> raise (Eval_error ("Expected number, got " ^ type_of v))
(** [make_rtd name fields ctor_params] — create a record type descriptor.
Called as [make-rtd] from transpiled evaluator. Takes 3 separate args. *)
let make_rtd name fields ctor_params =
let uid = !rtd_counter in
incr rtd_counter;
let field_names = List.map value_to_string (match fields with List l -> l | _ -> []) in
let ctor_names = List.map value_to_string (match ctor_params with List l -> l | _ -> []) in
let field_arr = Array.of_list field_names in
let ctor_map = Array.of_list (List.map (fun cp ->
let rec find j = function
| [] -> raise (Eval_error (Printf.sprintf "make-rtd: ctor param %s not in fields" cp))
| f :: _ when f = cp -> j
| _ :: rest -> find (j + 1) rest
in find 0 field_names
) ctor_names) in
let rt = { rt_name = value_to_string name; rt_uid = uid; rt_fields = field_arr; rt_ctor_map = ctor_map } in
Hashtbl.add rtd_table uid rt;
Number (float_of_int uid)
(** [make_record uid_val args_list] — create a record from uid + args list.
2-arg direct call: (make-record rtd-uid ctor-args-list). *)
let make_record uid_val args_list =
let uid = val_to_int uid_val in
let ctor_args = match args_list with List l -> l | _ -> [] in
match Hashtbl.find_opt rtd_table uid with
| None -> raise (Eval_error "make-record: unknown rtd")
| Some rt ->
let n_ctor = Array.length rt.rt_ctor_map in
let n_args = List.length ctor_args in
if n_args <> n_ctor then
raise (Eval_error (Printf.sprintf "%s: expected %d args, got %d"
rt.rt_name n_ctor n_args));
let fields = Array.make (Array.length rt.rt_fields) Nil in
List.iteri (fun i arg ->
fields.(rt.rt_ctor_map.(i)) <- arg
) ctor_args;
Record { r_type = rt; r_fields = fields }
(** [record_ref v idx] — access field by index. 2-arg direct call. *)
let record_ref v idx =
match v with
| Record r ->
let i = val_to_int idx in
if i < 0 || i >= Array.length r.r_fields then
raise (Eval_error (Printf.sprintf "record-ref: index %d out of bounds for %s" i r.r_type.rt_name));
r.r_fields.(i)
| _ -> raise (Eval_error ("record-ref: not a record, got " ^ type_of v))
(** [record_set_b v idx new_val] — mutate field by index. 3-arg direct call.
Named record_set_b because transpiler mangles record-set! to record_set_b. *)
let record_set_b v idx new_val =
match v with
| Record r ->
let i = val_to_int idx in
if i < 0 || i >= Array.length r.r_fields then
raise (Eval_error (Printf.sprintf "record-set!: index %d out of bounds for %s" i r.r_type.rt_name));
r.r_fields.(i) <- new_val; Nil
| _ -> raise (Eval_error ("record-set!: not a record, got " ^ type_of v))
(** [record_type_p v uid_val] — type predicate. 2-arg direct call.
Named record_type_p because transpiler mangles record-type? to record_type_p. *)
let record_type_p v uid_val =
match v with
| Record r -> Bool (r.r_type.rt_uid = val_to_int uid_val)
| _ -> Bool false
(** [record_p v] — generic record predicate.
Named record_p because transpiler mangles record? to record_p. *)
let record_p v = Bool (is_record v)
(** [make_record_constructor rtd_uid] — returns a NativeFn that constructs records.
Called from transpiled sf-define-record-type. *)
let make_record_constructor uid_val =
let uid = val_to_int uid_val in
let rt = match Hashtbl.find_opt rtd_table uid with
| Some rt -> rt | None -> raise (Eval_error "make-record-constructor: unknown rtd") in
NativeFn (rt.rt_name, fun args ->
let n_ctor = Array.length rt.rt_ctor_map in
let n_args = List.length args in
if n_args <> n_ctor then
raise (Eval_error (Printf.sprintf "%s: expected %d args, got %d" rt.rt_name n_ctor n_args));
let fields = Array.make (Array.length rt.rt_fields) Nil in
List.iteri (fun i arg -> fields.(rt.rt_ctor_map.(i)) <- arg) args;
Record { r_type = rt; r_fields = fields })
(** [make_record_predicate rtd_uid] — returns a NativeFn that tests record type. *)
let make_record_predicate uid_val =
let uid = val_to_int uid_val in
NativeFn ("?", fun args ->
match args with
| [Record r] -> Bool (r.r_type.rt_uid = uid)
| [_] -> Bool false
| _ -> raise (Eval_error "record predicate: expected 1 arg"))
(** [make_record_accessor field_idx] — returns a NativeFn that reads a field. *)
let make_record_accessor idx_val =
let idx = val_to_int idx_val in
NativeFn ("ref", fun args ->
match args with
| [Record r] ->
if idx < 0 || idx >= Array.length r.r_fields then
raise (Eval_error (Printf.sprintf "record accessor: index %d out of bounds" idx));
r.r_fields.(idx)
| [v] -> raise (Eval_error ("record accessor: not a record, got " ^ type_of v))
| _ -> raise (Eval_error "record accessor: expected 1 arg"))
(** [make_record_mutator field_idx] — returns a NativeFn that sets a field. *)
let make_record_mutator idx_val =
let idx = val_to_int idx_val in
NativeFn ("set!", fun args ->
match args with
| [Record r; new_val] ->
if idx < 0 || idx >= Array.length r.r_fields then
raise (Eval_error (Printf.sprintf "record mutator: index %d out of bounds" idx));
r.r_fields.(idx) <- new_val; Nil
| _ -> raise (Eval_error "record mutator: expected (record value)"))
(** {1 Dict operations} *)
let make_dict () : dict = Hashtbl.create 8
@@ -541,3 +690,8 @@ let rec inspect = function
| CekState _ -> "<cek-state>"
| CekFrame f -> Printf.sprintf "<frame:%s>" f.cf_type
| VmClosure cl -> Printf.sprintf "<vm:%s>" (match cl.vm_name with Some n -> n | None -> "anon")
| Record r ->
let fields = Array.to_list (Array.mapi (fun i v ->
Printf.sprintf "%s=%s" r.r_type.rt_fields.(i) (inspect v)
) r.r_fields) in
Printf.sprintf "<record:%s %s>" r.r_type.rt_name (String.concat " " fields)