sx-http: CSSX source before components in defs, JIT closure bug identified

Move client library sources (cssx.sx) before defcomp/defisland
definitions in component-defs so defines are evaluated first.

Identified root cause of CSSX "Not callable: nil" errors:
JIT compiler captures free variable values at compile time instead of
looking them up at runtime from vm_globals. When ~cssx/tw's JIT code
calls cssx-process-token, it uses the compile-time snapshot (nil)
instead of the runtime value (lambda). The function IS in global_env
(type-of returns "lambda") but the JIT bytecode doesn't see it.

Fix needed: JIT compiler should emit GLOBAL_GET instructions for free
variables that reference vm_globals at runtime, not capture at compile.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-28 20:04:35 +00:00
parent 6a22699587
commit f3e516feec

View File

@@ -1653,8 +1653,24 @@ let read_css_file path =
(** Pre-compute shell statics and inject into env as __shell-* vars. *)
let http_inject_shell_statics env static_dir sx_sxc =
(* Component definitions for client *)
(* Component definitions for client.
Client library sources FIRST (CSSX etc.) so defines are available
before defcomp/defisland bodies that reference them. *)
let buf = Buffer.create 65536 in
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
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
Buffer.add_string buf src;
Buffer.add_char buf '\n'
end
) client_libs;
(* Then component/island definitions *)
Hashtbl.iter (fun _sym v ->
match v with
| Component c ->
@@ -1671,22 +1687,6 @@ let http_inject_shell_statics env static_dir sx_sxc =
i.i_name ps (serialize_value i.i_body))
| _ -> ()
) env.bindings;
(* Prepend client library source files — these define functions that
island components need at runtime (CSSX, signals, etc.).
Read directly from .sx files, same as Quart's _CLIENT_LIBRARY_SOURCES. *)
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
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
Buffer.add_string buf src;
Buffer.add_char buf '\n'
end
) client_libs;
let raw_defs = Buffer.contents buf in
(* Component-defs are inlined in <script type="text/sx">.
The escape_sx_string function handles </ → <\\/ inside string