host: enable reitit — runtime JOLT_FEATURES, class-shim hooks, get-on-strings, java shims

Everything reitit-core needs to load unmodified from git under :clj features:

- The baked binary now re-reads JOLT_FEATURES at startup (like JOLT_PATH).
  reader-features-set! runs at module load = BUILD time for a binary, so a
  process opting into :clj (to read a lib's :clj branches) was ignored, and
  unmatched #?(...) forms silently spliced to nothing — defn of a fn with an
  empty arglist, hence the cryptic index errors.

- (get s i) indexes a string and returns the char, as in Clojure (nth did;
  get returned nil). reitit's path parser is (get path i)-based — without
  this every route read as static.

- Class-shim registration exposed to Clojure: __register-class-statics! /
  __register-class-methods! / __register-class-ctor!, so a library can mirror
  a Java class jolt doesn't ship (the reitit.Trie mirror lives in jolt-lang/
  router on top of these).

- Java surface reitit's :clj branches call: .getMessage (on exceptions and
  strings) and a small universal object-method set, .intern, java.util.HashMap
  (a mutable map wrapper). Plus defprotocol already took keyword options.

Gate green; clojure-test-suite 4715 -> 4718 (the get-on-strings fix).
This commit is contained in:
Yogthos 2026-06-12 01:08:09 -04:00
parent 0ad6529d44
commit 6ab76efd19
7 changed files with 101 additions and 7 deletions

View file

@ -199,3 +199,16 @@
"(do (janet.os/setenv \"JOLT_BAKE_ENV_ALLOWLIST\" \"HOME\") (let [e (System/getenv)] (janet.os/setenv \"JOLT_BAKE_ENV_ALLOWLIST\" nil) (and (contains? (set (keys e)) \"HOME\") (= 1 (count (keys e))))))"]
["no allowlist: unfiltered live reads" "true"
"(string? (System/getenv \"HOME\"))"])
# Host-class shim registration exposed to Clojure (reitit.Trie mirror, etc.):
# statics resolve as (Class/method ...), ctors as (Class. ...), and registered
# tag methods dispatch. Also: .getMessage on an exception/string, HashMap.
(defspec "host-interop / exception + HashMap shims"
["getMessage on a thrown string" "\"boom\""
"(try (throw \"boom\") (catch Throwable e (.getMessage e)))"]
["getMessage on ex-info" "\"bad\""
"(try (throw (ex-info \"bad\" {})) (catch Throwable e (.getMessage e)))"]
["HashMap get" "2"
"(let [m (HashMap. {:a 1 :b 2})] (.get m :b))"]
["HashMap put + size" "2"
"(let [m (HashMap. {})] (.put m :x 1) (.put m :y 2) (.size m))"])

View file

@ -53,3 +53,12 @@
["hyphens to underscores" "\"a_b_c\"" "(namespace-munge \"a-b-c\")"]
["from a symbol" "\"foo_bar\"" "(namespace-munge (quote foo-bar))"]
["no hyphens unchanged" "\"ok\"" "(namespace-munge \"ok\")"])
# (get s i) indexes a string and returns the char, like Clojure (nth already
# did; get did not — reitit's path parser relies on it).
(defspec "strings / get indexes a string"
["get returns the char" "true" "(= (get \"a:b\" 1) \\:)"]
["get first char" "\\a" "(get \"abc\" 0)"]
["get out of range nil" "nil" "(get \"abc\" 9)"]
["get negative nil" "nil" "(get \"abc\" -1)"]
["get default honored" ":none" "(get \"abc\" 9 :none)"])