Restore all OCaml + request-handler to working state (aa4c911)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -713,13 +713,13 @@ let register_jit_hook env =
|
||||
Sx_ref.jit_call_hook := Some (fun f args ->
|
||||
match f with
|
||||
| Lambda l ->
|
||||
let fn_name = match l.l_name with Some n -> n | None -> "?" in
|
||||
(match l.l_compiled with
|
||||
| Some cl when not (Sx_vm.is_jit_failed cl) ->
|
||||
(* Cached bytecode — run on VM, fall back to CEK on runtime error.
|
||||
Log once per function name, then stay quiet. Don't disable. *)
|
||||
(try Some (Sx_vm.call_closure cl args cl.vm_env_ref)
|
||||
with e ->
|
||||
let fn_name = match l.l_name with Some n -> n | None -> "?" in
|
||||
if not (Hashtbl.mem _jit_warned fn_name) then begin
|
||||
Hashtbl.replace _jit_warned fn_name true;
|
||||
Printf.eprintf "[jit] %s runtime fallback to CEK: %s\n%!" fn_name (Printexc.to_string e)
|
||||
@@ -727,8 +727,8 @@ let register_jit_hook env =
|
||||
None)
|
||||
| Some _ -> None (* compile failed or disabled — CEK handles *)
|
||||
| None ->
|
||||
let fn_name = match l.l_name with Some n -> n | None -> "?" in
|
||||
if !_jit_compiling then None
|
||||
else if Hashtbl.mem _jit_warned fn_name then None
|
||||
else begin
|
||||
_jit_compiling := true;
|
||||
let t0 = Unix.gettimeofday () in
|
||||
@@ -743,7 +743,6 @@ let register_jit_hook env =
|
||||
(try Some (Sx_vm.call_closure cl args cl.vm_env_ref)
|
||||
with e ->
|
||||
Printf.eprintf "[jit] %s first-call fallback to CEK: %s\n%!" fn_name (Printexc.to_string e);
|
||||
l.l_compiled <- Some Sx_vm.jit_failed_sentinel;
|
||||
Hashtbl.replace _jit_warned fn_name true;
|
||||
None)
|
||||
| None -> None
|
||||
@@ -1579,16 +1578,9 @@ let http_inject_shell_statics env static_dir sx_sxc =
|
||||
let project_dir = try Sys.getenv "SX_PROJECT_DIR" with Not_found ->
|
||||
Filename.dirname (Filename.dirname static_dir) in
|
||||
let templates_dir = project_dir ^ "/shared/sx/templates" in
|
||||
(* Client libraries: all .sx files in templates/client-libs/ *)
|
||||
let client_libs_dir = templates_dir ^ "/client-libs" in
|
||||
let extra_libs =
|
||||
if Sys.file_exists client_libs_dir && Sys.is_directory client_libs_dir then
|
||||
Array.to_list (Sys.readdir client_libs_dir)
|
||||
|> List.filter (fun f -> Filename.check_suffix f ".sx")
|
||||
|> List.sort String.compare
|
||||
|> List.map (fun f -> client_libs_dir ^ "/" ^ f)
|
||||
else [] in
|
||||
let client_libs = (templates_dir ^ "/cssx.sx") :: extra_libs in
|
||||
let client_libs = [
|
||||
templates_dir ^ "/cssx.sx";
|
||||
] in
|
||||
List.iter (fun path ->
|
||||
if Sys.file_exists path then begin
|
||||
let src = In_channel.with_open_text path In_channel.input_all in
|
||||
@@ -1622,20 +1614,7 @@ let http_inject_shell_statics env static_dir sx_sxc =
|
||||
(* Compute file hashes for cache busting *)
|
||||
let sx_js_hash = file_hash (static_dir ^ "/scripts/sx-browser.js") in
|
||||
let body_js_hash = file_hash (static_dir ^ "/scripts/body.js") in
|
||||
(* Include SX source file hashes so browser cache busts when .sx files change *)
|
||||
let sx_dir = static_dir ^ "/wasm/sx" in
|
||||
let sx_files_hash =
|
||||
if Sys.file_exists sx_dir && Sys.is_directory sx_dir then
|
||||
let entries = Sys.readdir sx_dir in
|
||||
Array.sort String.compare entries;
|
||||
let combined = Array.fold_left (fun acc f ->
|
||||
if Filename.check_suffix f ".sx" then
|
||||
acc ^ file_hash (sx_dir ^ "/" ^ f)
|
||||
else acc
|
||||
) "" entries in
|
||||
String.sub (Digest.string combined |> Digest.to_hex) 0 12
|
||||
else "" in
|
||||
let wasm_hash = file_hash (static_dir ^ "/wasm/sx_browser.bc.wasm.js") ^ sx_files_hash in
|
||||
let wasm_hash = file_hash (static_dir ^ "/wasm/sx_browser.bc.wasm.js") in
|
||||
(* Read CSS for inline injection *)
|
||||
let tw_css = read_css_file (static_dir ^ "/styles/tw.css") in
|
||||
let basics_css = read_css_file (static_dir ^ "/styles/basics.css") in
|
||||
@@ -1695,7 +1674,16 @@ let http_inject_shell_statics env static_dir sx_sxc =
|
||||
ignore (env_bind env "__shell-body-scripts" Nil);
|
||||
ignore (env_bind env "__shell-inline-css" Nil);
|
||||
ignore (env_bind env "__shell-inline-head-js" Nil);
|
||||
ignore (env_bind env "__shell-init-sx" Nil);
|
||||
(* init-sx: trigger client-side render when sx-root is empty (SSR failed).
|
||||
The boot code hydrates existing islands but doesn't do fresh render.
|
||||
This script forces a render from page-sx after boot completes. *)
|
||||
ignore (env_bind env "__shell-init-sx" (String
|
||||
"document.addEventListener('sx:boot-done', function() { \
|
||||
var root = document.getElementById('sx-root'); \
|
||||
if (root && !root.innerHTML.trim() && typeof SX !== 'undefined' && SX.renderPage) { \
|
||||
SX.renderPage(); \
|
||||
} \
|
||||
});"));
|
||||
Printf.eprintf "[sx-http] Shell statics: defs=%d hash=%s css=%d js=%s wasm=%s\n%!"
|
||||
(String.length component_defs) component_hash (String.length sx_css) sx_js_hash wasm_hash
|
||||
|
||||
@@ -1801,9 +1789,7 @@ let http_setup_page_helpers env =
|
||||
SxExpr (Printf.sprintf "(pre :class \"text-sm overflow-x-auto\" (code \"%s\"))" escaped)
|
||||
| _ -> Nil);
|
||||
(* component-source — stub *)
|
||||
bind "component-source" (fun _args -> String "");
|
||||
(* handler-source — stub (returns empty, used by example pages) *)
|
||||
bind "handler-source" (fun _args -> String "")
|
||||
bind "component-source" (fun _args -> String "")
|
||||
|
||||
let http_mode port =
|
||||
let env = make_server_env () in
|
||||
@@ -2022,9 +2008,7 @@ let http_mode port =
|
||||
let is_ajax = List.exists (fun (k, _) -> k = "sx-request" || k = "hx-request") headers in
|
||||
match http_render_page env path headers with
|
||||
| Some html ->
|
||||
let ct = if is_ajax then "text/sx; charset=utf-8"
|
||||
else "text/html; charset=utf-8" in
|
||||
let resp = http_response ~content_type:ct html in
|
||||
let resp = http_response ~content_type:"text/html; charset=utf-8" html in
|
||||
if not is_ajax then Hashtbl.replace response_cache path resp;
|
||||
resp
|
||||
| None -> http_response ~status:404 "<h1>Not Found</h1>"
|
||||
|
||||
@@ -280,10 +280,7 @@ and render_list_to_html head args env =
|
||||
| _ ->
|
||||
let result = Sx_ref.eval_expr (List (head :: args)) (Env env) in
|
||||
do_render_to_html result env)
|
||||
with Eval_error _ ->
|
||||
(* Symbol not in env — might be a primitive; eval the full expression *)
|
||||
let result = Sx_ref.eval_expr (List (head :: args)) (Env env) in
|
||||
do_render_to_html result env)
|
||||
with Eval_error _ -> "")
|
||||
| _ ->
|
||||
let result = Sx_ref.eval_expr (List (head :: args)) (Env env) in
|
||||
do_render_to_html result env
|
||||
@@ -533,13 +530,10 @@ and render_list_buf buf head args env =
|
||||
| _ ->
|
||||
let result = Sx_ref.eval_expr (List (head :: args)) (Env env) in
|
||||
render_to_buf buf result env)
|
||||
with Eval_error _ ->
|
||||
(* Symbol not in env — might be a primitive; eval the full expression *)
|
||||
(try
|
||||
let result = Sx_ref.eval_expr (List (head :: args)) (Env env) in
|
||||
render_to_buf buf result env
|
||||
with Eval_error msg ->
|
||||
Printf.eprintf "[ssr-skip] %s\n%!" msg))
|
||||
with Eval_error msg ->
|
||||
(* Unknown symbol/component — skip silently during SSR.
|
||||
The client will render from page-sx. *)
|
||||
Printf.eprintf "[ssr-skip] %s\n%!" msg)
|
||||
| _ ->
|
||||
(try
|
||||
let result = Sx_ref.eval_expr (List (head :: args)) (Env env) in
|
||||
|
||||
@@ -576,12 +576,14 @@ let jit_compile_lambda (l : lambda) globals =
|
||||
let fn_expr = List [Symbol "fn"; param_syms; l.l_body] in
|
||||
let quoted = List [Symbol "quote"; fn_expr] in
|
||||
let result = Sx_ref.eval_expr (List [compile_fn; quoted]) (Env (make_env ())) in
|
||||
(* Inject closure bindings into globals so GLOBAL_GET can find them.
|
||||
Only injects values not already present in globals (preserves
|
||||
existing defines). Mutable closure vars get stale snapshots here
|
||||
but GLOBAL_SET writes back to vm_closure_env, and GLOBAL_GET
|
||||
falls through to vm_closure_env if the global is stale. *)
|
||||
(* If the lambda has closure-captured variables, merge them into globals
|
||||
so the VM can find them via GLOBAL_GET. The compiler doesn't know
|
||||
about the enclosing scope, so closure vars get compiled as globals. *)
|
||||
let effective_globals =
|
||||
(* Use the LIVE globals table directly. Inject only truly local
|
||||
closure bindings (not already in globals) into the live table.
|
||||
This ensures GLOBAL_GET always sees the latest define values.
|
||||
Previous approach copied globals, creating a stale snapshot. *)
|
||||
let closure = l.l_closure in
|
||||
let count = ref 0 in
|
||||
let rec inject env =
|
||||
@@ -623,14 +625,16 @@ let jit_compile_lambda (l : lambda) globals =
|
||||
as a NativeFn if it's callable (so the CEK can dispatch to it). *)
|
||||
(try
|
||||
let value = execute_module outer_code globals in
|
||||
ignore (fn_name, value, bc); (* resolved — not a closure, CEK handles it *)
|
||||
Printf.eprintf "[jit] RESOLVED %s: %s (bc[0]=%d)\n%!"
|
||||
fn_name (type_of value) (if Array.length bc > 0 then bc.(0) else -1);
|
||||
(* If the resolved value is a NativeFn, we can't wrap it as a
|
||||
vm_closure — just let the CEK handle it directly. Return None
|
||||
so the lambda falls through to CEK, which will find the
|
||||
resolved value in the env on next lookup. *)
|
||||
None
|
||||
with _ ->
|
||||
ignore fn_name; (* non-closure, execution failed — CEK fallback *)
|
||||
Printf.eprintf "[jit] SKIP %s: non-closure execution failed (bc[0]=%d, len=%d)\n%!"
|
||||
fn_name (if Array.length bc > 0 then bc.(0) else -1) (Array.length bc);
|
||||
None)
|
||||
end
|
||||
| _ ->
|
||||
|
||||
Reference in New Issue
Block a user