host interop + deps fixes for running ring-defaults on jolt

Shaken out getting ring-defaults (and its ring-core/anti-forgery/session stack)
to load and serve static resources on jolt. All general fixes, all runtime:

- Class/forName throws a catchable ClassNotFoundException for a class jolt can't
  back (it returned a broken truthy value for any name, and crashed on use). Lets
  the common (try (Class/forName "optional.Dep") (catch ...)) probe libraries use
  to detect an absent dependency work — e.g. ring's joda-time check.
- deps: reconcile native libs (and source roots) in one step, deduped by library
  identity, instead of the ad-hoc distinct at each call site. An app pulling two
  libs that declare the same shared object (libcrypto via both jolt-crypto and
  http-client) now includes and loads it once.
- io: a File answers getProtocol ("file") / getFile so resource-serving
  middleware that expects io/resource to hand back a file: URL works; the
  classloader gains getResources (every source root holding the resource).
- clojure.string/replace accepts a char match/replacement, like the JVM.

JVM-certified corpus rows for the Class/forName and string/replace behavior.
This commit is contained in:
Yogthos 2026-06-25 04:42:35 -04:00
parent 16528e8637
commit 635cab0e49
5 changed files with 72 additions and 14 deletions

View file

@ -206,13 +206,24 @@
(register-class-statics! "NumberFormat" nf-statics)
(register-class-statics! "java.text.NumberFormat" nf-statics))
;; Class.forName: an array descriptor ("[C") is its own class token; a class Jolt
;; can back (registered statics/ctor, or a java.*/clojure.* core class) yields a
;; class object; anything else throws a catchable ClassNotFoundException, like the
;; JVM — so the common `(try (Class/forName "optional.Dep") (catch …))` probe a
;; library uses to detect an absent dependency works (e.g. ring's joda-time check).
(define (forname-known? nm)
(or (lookup-class class-statics-tbl nm)
(lookup-class class-ctors-tbl nm)
(let ((pre? (lambda (p) (and (>= (string-length nm) (string-length p))
(string=? (substring nm 0 (string-length p)) p)))))
(or (pre? "java.") (pre? "clojure.") (pre? "jolt.")))))
(register-class-statics! "Class"
;; an array descriptor ("[C", "[I", …) is its own class token (so instance? and
;; class compare equal); other names become a class jhost.
(list (cons "forName" (lambda (nm)
(if (and (> (string-length nm) 0) (char=? (string-ref nm 0) #\[))
nm
(make-jhost "class" (list (cons 'name nm))))))))
(list (cons "forName"
(lambda (nm . _)
(cond
((and (> (string-length nm) 0) (char=? (string-ref nm 0) #\[)) nm)
((forname-known? nm) (make-class-obj nm))
(else (jolt-throw (jolt-host-throwable "java.lang.ClassNotFoundException" nm))))))))
;; ---- System helpers (defined before use above via top-level order) ----------
;; os.name reflects the actual platform (Chez's machine-type names it): a *osx