sx-http: JIT enabled, signals loaded, recursive dir loading, assoc fix

- Enable lazy JIT in HTTP mode — pre-compile 24 compiler functions at startup
- Load spec/signals.sx + web/engine.sx for reactive primitives
- Recursive directory loading for subdirectory components (geography/, etc.)
- Re-bind native variadic assoc after stdlib.sx overwrites it
- Skip test files, plans/, essays/ directories during HTTP load
- Homepage aser: 21-38ms warm, Geography aser: 39-87ms warm

Remaining: render-to-html JIT gets disabled by <home symbol error on
first request (falls back to CEK). ~docs/page component missing for
some pages. Fix those for full parity with Quart.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-28 16:32:41 +00:00
parent 0d5770729f
commit 9fc13efa1a

View File

@@ -1701,29 +1701,41 @@ let http_mode port =
let sx_sx = project_dir ^ "/sx/sx" in
let t0 = Unix.gettimeofday () in
(* Core spec + adapters.
Skip primitives.sx (declarative metadata — all prims are native in OCaml)
and types.sx (gradual type system — not needed for rendering). *)
Skip: primitives.sx (declarative metadata — all prims native in OCaml),
types.sx (gradual types — not needed for rendering),
evaluator.sx (SX-level CEK — native evaluator already compiled in).
The native CEK evaluator is faster — evaluator.sx adds a second SX-level
stepper that's 100x slower. *)
let core_files = [
spec_base ^ "/parser.sx";
spec_base ^ "/render.sx"; spec_base ^ "/evaluator.sx";
spec_base ^ "/render.sx";
spec_base ^ "/signals.sx";
lib_base ^ "/compiler.sx";
web_base ^ "/adapter-html.sx"; web_base ^ "/adapter-sx.sx";
web_base ^ "/web-forms.sx";
web_base ^ "/web-forms.sx"; web_base ^ "/engine.sx";
] in
http_load_files env core_files;
(* Libraries *)
(* Files to skip — declarative metadata, not needed for rendering *)
let skip_files = ["primitives.sx"; "types.sx"; "boundary.sx";
"harness.sx"; "eval-rules.sx"] in
let load_dir dir =
"harness.sx"; "eval-rules.sx"; "vm-inline.sx"] in
let skip_dirs = ["tests"; "test"; "plans"; "essays"; "spec"] in
let rec load_dir dir =
if Sys.file_exists dir && Sys.is_directory dir then begin
let files = Sys.readdir dir in
Array.sort String.compare files;
let entries = Sys.readdir dir in
Array.sort String.compare entries;
Array.iter (fun f ->
if Filename.check_suffix f ".sx"
&& not (List.mem f skip_files)
&& not (Filename.check_suffix f ".test.sx") then
http_load_files env [dir ^ "/" ^ f]
) files
let path = dir ^ "/" ^ f in
if Sys.is_directory path then begin
if not (List.mem f skip_dirs) then
load_dir path
end
else if Filename.check_suffix f ".sx"
&& not (List.mem f skip_files)
&& not (String.length f > 5 && String.sub f 0 5 = "test-")
&& not (Filename.check_suffix f ".test.sx") then
http_load_files env [path]
) entries
end
in
load_dir lib_base;
@@ -1731,6 +1743,57 @@ let http_mode port =
load_dir sx_sx;
let t1 = Unix.gettimeofday () in
Printf.eprintf "[sx-http] All files loaded in %.3fs\n%!" (t1 -. t0);
(* Enable lazy JIT — compile lambdas to bytecode on first call *)
register_jit_hook env;
let jt0 = Unix.gettimeofday () in
let count = ref 0 in
let compiler_names = [
"compile"; "compile-module"; "compile-expr"; "compile-symbol";
"compile-dict"; "compile-list"; "compile-if"; "compile-when";
"compile-and"; "compile-or"; "compile-begin"; "compile-let";
"compile-letrec"; "compile-lambda"; "compile-define"; "compile-set";
"compile-quote"; "compile-cond"; "compile-case"; "compile-case-clauses";
"compile-thread"; "compile-thread-step"; "compile-defcomp";
"compile-defisland"; "compile-defmacro";
] in
List.iter (fun name ->
try
match env_get env name with
| Lambda l ->
l.l_compiled <- Some Sx_vm.jit_failed_sentinel;
(match Sx_vm.jit_compile_lambda l (env_to_vm_globals env) with
| Some cl -> l.l_compiled <- Some cl; incr count
| None -> ())
| _ -> ()
with _ -> ()
) compiler_names;
let jt1 = Unix.gettimeofday () in
Printf.eprintf "[sx-http] JIT pre-compiled %d compiler fns in %.3fs\n%!" !count (jt1 -. jt0);
(* Re-bind native primitives that stdlib.sx may have overwritten with
narrower SX versions. The native assoc handles variadic key/value pairs
which evaluator.sx requires. *)
rebind_host_extensions env;
ignore (env_bind env "assoc" (NativeFn ("assoc", fun args ->
match args with
| Dict d :: rest ->
let d2 = Hashtbl.copy d in
let rec go = function
| [] -> Dict d2
| String k :: v :: rest -> Hashtbl.replace d2 k v; go rest
| Keyword k :: v :: rest -> Hashtbl.replace d2 k v; go rest
| _ -> raise (Eval_error "assoc: pairs")
in go rest
| _ -> raise (Eval_error "assoc: dict + pairs"))));
(* Also re-bind highlight from SX lib if loaded *)
(try
let hl = env_get env "highlight" in
ignore hl (* already bound by lib/highlight.sx *)
with _ ->
(* Fallback: passthrough highlight *)
ignore (env_bind env "highlight" (NativeFn ("highlight", fun args ->
match args with
| String code :: _ -> SxExpr (Printf.sprintf "(pre :class \"text-sm overflow-x-auto\" (code \"%s\"))" (escape_sx_string code))
| _ -> Nil))));
(* Inject shell statics *)
http_inject_shell_statics env;
(* Start TCP server *)