Expand JIT closure tests: nested, mutual recursion, set!, island, deep
22 JIT closure scoping tests covering: - Basic closure var in map callback + context switch - Signal + letrec + map (stepper pattern) - Nested closures (inner lambda sees outer let var) - Mutual recursion in letrec (is-even/is-odd) - set! mutation of closure var after JIT compilation - defisland with signal + letrec + map - Deep nesting (for-each inside map inside letrec inside let) All test the critical invariant: JIT-compiled lambdas must use their closure's vm_env_ref, not the caller's globals. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -555,6 +555,94 @@ let () =
|
||||
assert_contains "jit signal closure: after other render"
|
||||
"4" (let _ = sx_render_html "(div \"break\")" in sx_render_html "(~jit-signal-test)");
|
||||
|
||||
(* 6. Nested closures — lambda inside lambda, both with closure vars *)
|
||||
assert_no_error "defcomp with nested closures" (fun () ->
|
||||
ignore (Sx_ref.eval_expr
|
||||
(List.hd (Sx_parser.parse_all
|
||||
"(defcomp ~jit-nested (&key)
|
||||
(let ((prefix \">\"))
|
||||
(letrec ((wrap (fn (x)
|
||||
(let ((suffix \"<\"))
|
||||
(str prefix x suffix)))))
|
||||
(div (map (fn (item) (span (wrap item)))
|
||||
(list \"a\" \"b\"))))))"))
|
||||
(Env env)));
|
||||
assert_contains "nested closure: inner sees outer var"
|
||||
">a<" (sx_render_html "(~jit-nested)");
|
||||
assert_contains "nested closure: second item"
|
||||
">b<" (sx_render_html "(~jit-nested)");
|
||||
(* After unrelated render, nested closures still work *)
|
||||
assert_contains "nested closure: survives context switch"
|
||||
">a<" (let _ = sx_render_html "(p \"x\")" in sx_render_html "(~jit-nested)");
|
||||
|
||||
(* 7. Mutual recursion in letrec *)
|
||||
assert_no_error "defcomp with mutual recursion" (fun () ->
|
||||
ignore (Sx_ref.eval_expr
|
||||
(List.hd (Sx_parser.parse_all
|
||||
"(defcomp ~jit-mutual (&key)
|
||||
(letrec ((is-even (fn (n)
|
||||
(if (= n 0) true (is-odd (- n 1)))))
|
||||
(is-odd (fn (n)
|
||||
(if (= n 0) false (is-even (- n 1))))))
|
||||
(div
|
||||
(span (str (is-even 4)))
|
||||
(span (str (is-odd 3))))))"))
|
||||
(Env env)));
|
||||
assert_contains "mutual recursion: is-even 4" "true" (sx_render_html "(~jit-mutual)");
|
||||
assert_contains "mutual recursion: is-odd 3" "true" (sx_render_html "(~jit-mutual)");
|
||||
assert_contains "mutual recursion: survives context switch"
|
||||
"true" (let _ = sx_render_html "(div \"y\")" in sx_render_html "(~jit-mutual)");
|
||||
|
||||
(* 8. set! modifying closure var after JIT compilation *)
|
||||
assert_no_error "defcomp with set! mutation" (fun () ->
|
||||
ignore (Sx_ref.eval_expr
|
||||
(List.hd (Sx_parser.parse_all
|
||||
"(defcomp ~jit-setbang (&key)
|
||||
(let ((counter 0))
|
||||
(letrec ((bump (fn () (set! counter (+ counter 1)) counter))
|
||||
(get-count (fn () counter)))
|
||||
(div (span (str (bump)))
|
||||
(span (str (bump)))
|
||||
(span (str (get-count)))))))"))
|
||||
(Env env)));
|
||||
(* Each render should restart counter at 0 since it's a fresh let *)
|
||||
assert_contains "set! mutation: first bump" "1" (sx_render_html "(~jit-setbang)");
|
||||
assert_contains "set! mutation: second bump" "2" (sx_render_html "(~jit-setbang)");
|
||||
|
||||
(* 9. Island with signal + effect + letrec — the stepper pattern *)
|
||||
assert_no_error "defisland with signal+letrec+map" (fun () ->
|
||||
ignore (Sx_ref.eval_expr
|
||||
(List.hd (Sx_parser.parse_all
|
||||
"(defisland ~jit-island-test ()
|
||||
(let ((items (signal (list \"x\" \"y\" \"z\")))
|
||||
(label (signal \"test\")))
|
||||
(letrec ((format-item (fn (item)
|
||||
(str (deref label) \":\" item))))
|
||||
(div (map (fn (i) (span (format-item i)))
|
||||
(deref items))))))"))
|
||||
(Env env)));
|
||||
assert_contains "island signal+letrec: renders"
|
||||
"test:x" (sx_render_html "(~jit-island-test)");
|
||||
assert_contains "island signal+letrec: after other render"
|
||||
"test:y" (let _ = sx_render_html "(p \"z\")" in sx_render_html "(~jit-island-test)");
|
||||
|
||||
(* 10. Deep nesting — for-each inside map inside letrec inside let *)
|
||||
assert_no_error "defcomp with deep nesting" (fun () ->
|
||||
ignore (Sx_ref.eval_expr
|
||||
(List.hd (Sx_parser.parse_all
|
||||
"(defcomp ~jit-deep (&key)
|
||||
(let ((rows (list (list 1 2) (list 3 4))))
|
||||
(letrec ((sum-row (fn (row)
|
||||
(reduce + 0 row))))
|
||||
(div (map (fn (row)
|
||||
(span (str (sum-row row))))
|
||||
rows)))))"))
|
||||
(Env env)));
|
||||
assert_contains "deep nesting: first row sum" "3" (sx_render_html "(~jit-deep)");
|
||||
assert_contains "deep nesting: second row sum" "7" (sx_render_html "(~jit-deep)");
|
||||
assert_contains "deep nesting: survives context switch"
|
||||
"3" (let _ = sx_render_html "(div \"w\")" in sx_render_html "(~jit-deep)");
|
||||
|
||||
(* ================================================================== *)
|
||||
Printf.printf "\n";
|
||||
Printf.printf "============================================================\n";
|
||||
|
||||
Reference in New Issue
Block a user