Chez Phase 2 (inc Q): Java class statics + constructors (jolt-avt6)

Lower host class interop on the Chez back end. The analyzer now turns a
non-var qualified ref `Class/member` into a :host-static node and a
`(Class. ...)` / `(new Class ...)` form into a :host-new node (ir.clj
gains both, with walker support). The Janet back end punts both to the
interpreter, so its behavior is unchanged (verified: dot-form, `..`
threading, shadowed `new`, and all interop still resolve via fallback).

The Chez emit lowers a value ref to host-static-ref, a call head to
host-static-call, and a constructor to host-new. host/chez/host-static.ss
is the runtime registry these resolve against — the Chez port of the
seed's class-statics / class-ctors / tagged-methods (java_base.janet +
host_io.janet), restricted to the java.lang/util/net/io surface portable
cljc code calls: Math, System (getenv/getProperty/exit/currentTimeMillis),
Long, Integer, Boolean, Character, String, Thread, Class, Pattern
(compile/quote/MULTILINE), URLEncoder/Decoder, Base64, the Number method
surface (byteValue/intValue/...), plus the StringBuilder, StringWriter,
StringReader, PushbackReader, HashMap, StringTokenizer, BigInteger,
String, MapEntry, and exception constructors. Constructed objects are
jhost records dispatched through record-method-dispatch.

Also: emit now evaluates collection-literal elements left-to-right
(emit-ordered) — Chez evaluates call args right-to-left, which had been
swapping side-effecting elements in [(read r) (read r)] and map literals.
This un-allowlisted the 6 eval-order corpus cases (the read-line trio +
the three map-construction cases). Removed `.write` from the
jolt-host-call fast-path so a StringWriter routes through dispatch.

java.time formatting, edn/read-over-readers, and slurp/with-open over
readers are deferred to a follow-up.

Corpus parity 2078 -> 2134 (floor raised), 0 new divergences; the
print-method builtin-override case is allowlisted (same multimethod gap,
newly reachable now that StringWriter constructs). emit-test 326/326,
_javastatic 51/51, conformance 355x3, full jpm test green.
This commit is contained in:
Yogthos 2026-06-18 23:24:01 -04:00
parent a706a79b90
commit c90c4cb610
10 changed files with 711 additions and 26 deletions

View file

@ -300,14 +300,17 @@
(string "chez=" out " janet=" want " | " err))))
# 3l) host interop method calls (inc 3h). (.method target arg*) analyzes to a
# :host-call IR node and lowers to a jolt-host-call dispatch. The Janet back end
# PUNTS these (no interop model -> interpreter); the Chez RT shims the methods
# jolt-core's io tier uses: .write -> display to a port, .isDirectory ->
# file-directory?, .listFiles -> directory-list. Interop has no portable oracle
# (the Janet host models it differently), so these are emit-shape checks plus one
# deterministic runtime probe (the root "/" is always a directory).
# :host-call IR node and lowers to a dispatch. The Janet back end PUNTS these
# (no interop model -> interpreter); the Chez RT shims the File methods
# jolt-core's io tier uses via jolt-host-call (.isDirectory -> file-directory?,
# .listFiles -> directory-list). All OTHER methods (.write/.append/.read/... on
# a StringWriter/StringReader/record) route through record-method-dispatch
# (jolt-avt6 removed .write from the jolt-host-call fast-path so a StringWriter
# jhost handles it). Interop has no portable oracle (the Janet host models it
# differently), so these are emit-shape checks plus one deterministic runtime
# probe (the root "/" is always a directory).
(each [label src needle]
[["emit .write -> jolt-host-call" "(fn [w x] (.write w x))" "jolt-host-call"]
[["emit .write -> record-method-dispatch" "(fn [w x] (.write w x))" "record-method-dispatch"]
["emit .write keeps method name" "(fn [w x] (.write w x))" "\"write\""]
["emit .isDirectory -> jolt-host-call" "(fn [f] (.isDirectory f))" "isDirectory"]
["emit .listFiles -> jolt-host-call" "(fn [f] (.listFiles f))" "listFiles"]]
@ -318,6 +321,21 @@
(ok "runtime .isDirectory \"/\" = true" (and (= code 0) (= out "true"))
(string "chez=" out " | " err)))
# 3l') host class statics + constructors (jolt-avt6). Class/member lowers to a
# :host-static node (host-static-ref in value position, host-static-call as a
# call head); (Class. ...) / (new Class ...) lower to :host-new. The Janet back
# end punts all three; the Chez RT resolves them from the class-statics /
# class-ctors / jhost-method registries (host/chez/host-static.ss). Emit-shape
# checks; the registry behaviour is covered by test/chez/_javastatic.janet.
(each [label src needle]
[["emit Class/field -> host-static-ref" "(fn [] Long/MAX_VALUE)" "(host-static-ref \"Long\" \"MAX_VALUE\")"]
["emit Class/method call -> host-static-call" "(fn [] (System/getenv))" "(host-static-call \"System\" \"getenv\")"]
["emit static call passes args" "(fn [x] (Long/parseLong x))" "(host-static-call \"Long\" \"parseLong\""]
["emit (Class.) -> host-new" "(fn [] (StringBuilder.))" "(host-new \"StringBuilder\")"]
["emit (new Class ...) -> host-new" "(fn [s] (new java.io.StringReader s))" "(host-new \"java.io.StringReader\""]]
(let [scm (protect (emit/emit (backend/analyze-form ctx (in (r/parse-next src) 0))))]
(ok label (and (scm 0) (string/find needle (scm 1))) (string/format "%p" scm))))
# 3m) regex (jolt-i0s3): the #"…" literal lowers to a jolt-regex value over the
# vendored irregex; re-pattern/re-matches/re-find/re-seq/regex? are def-var!'d
# into clojure.core (not subset native-ops — irregex's Unicode/property