Commit graph

16 commits

Author SHA1 Message Date
Yogthos
ec9fde9e7e Group the JVM interop shims under host/chez/java/
The host/chez directory mixed jolt's own runtime (value model, seq, reader,
vars, ns, multimethods) with the shims that emulate the JVM: java.* / javax.*
classes, clojure.lang interfaces, and the host-class registry they hang off.
Move that JVM-emulation layer into host/chez/java/ so it reads as a distinct
unit instead of being interleaved with the platform runtime.

Moved (content unchanged): host-static, host-static-methods,
host-static-classes, host-class, dot-forms, records-interop, byte-buffer,
io, io-streams, inst-time, java-time, bigdec, natives-queue, natives-str,
natives-array, math, concurrency, async, ffi.

The load paths in rt.ss/cli.ss and the build.ss runtime manifest are updated
to point at java/; the build inliner follows the (load ...) strings, so the
AOT path needs no other change. All runtime shims, no seed source touched
(the three .clj edits are doc comments), so no re-mint.

Gate green: make test (selfhost fixpoint, certify 0-new, sci 211, infer),
shakesmoke (4 apps byte-identical).
2026-06-25 18:35:44 -04:00
Yogthos
14ce46fb2a defn docstrings, assert throws AssertionError, Seqable covers collections
Conformance gaps surfaced re-running the library suites:

- defn now keeps a leading docstring as :doc metadata — it was dropped, so
  (:doc (meta #'f)) was always nil. Rides the def docstring slot.
- assert (and :pre/:post) throw a real AssertionError instead of an ex-info, so
  (catch AssertionError …) / (thrown? AssertionError …) match, with Clojure's
  "Assert failed: <msg>\n<form>" message.
- instance? clojure.lang.Seqable was conflated with ISeq, so a vector/map read
  as not-Seqable. Split them: Seqable covers every persistent collection, ISeq
  only seqs.
2026-06-25 17:23:24 -04:00
Yogthos
829c251bca Host shims and protocol fixes shaken out by aws-api
Running cognitect aws-api's pure test namespaces (signing/shapes/protocols/
util/retry/endpoints) surfaced general gaps:

- extend-protocol/extend-type accept a computed class type, e.g.
  (Class/forName "[B") for the byte-array class — the byte-array idiom data.json
  and aws-api use. The macro grouping handled only symbol/nil heads (it crashed on
  a list type); type->name resolves a Class value via .getName; a byte-array
  dispatches on the "[B" host tag.
- java.nio.ByteBuffer over a jolt byte-array (wrap/allocate/get/put/array/
  remaining/position/limit/duplicate/flip), plus extend-protocol to it.
- java.util.Arrays (equals/copyOf/copyOfRange/fill) and java.util.Random
  (nextBytes/nextInt/…).
- java.net.URI/create and clojure.lang.RT/baseLoader statics.
- clojure.core.async/promise-chan (deliver-once, peek-don't-pop).
- a failed java.time parse throws DateTimeParseException (typed), so
  (catch DateTimeParseException …) matches it instead of leaking an untyped
  condition.

The XML side lives in the jolt-lang/xml library (libxml2 over jolt.ffi); ByteBuffer
stays in core as a generic java.nio primitive.

Gate: make test green (corpus +6 JVM-certified rows, 0 NEW divergence; unit
553/553; SCI 211).
2026-06-25 16:56:48 -04:00
Yogthos
d21ab77e7e Run core.memoize's test suite on jolt
Shaking out clojure.core.memoize (207 assertions, 0 fail) cleared several
general gaps:

- deref/@ on a deftype or reify implementing clojure.lang.IDeref dispatches to
  its deref method (RetryingDelay / make-derefable).
- deftype mutable fields (^:unsynchronized-mutable / ^:volatile-mutable) are
  read live: a set! within a method is observed by a later read in the same
  invocation, not the entry-time capture. Needed for double-checked locking.
  Immutable fields stay let-bound. Field reads rewrite to (.-field inst) with
  lexical-shadow tracking.
- def metadata values are evaluated, like Clojure: ^{:k (f)} stores (f)'s
  result and ^{:af some-fn} the fn. :tag stays a literal hint.
- try dispatches catch clauses by class in order via the exception supertype
  hierarchy; a non-matching value re-throws, an untyped host condition is caught
  by a RuntimeException/Exception/Throwable clause. Previously the last clause
  won and the class was ignored.
- locking takes a real per-object monitor (recursive Chez mutex) now that
  futures/agents/threads share one heap; it was a no-op.
- supers/ancestors reflect a small modeled JVM interface hierarchy, so
  (ancestors (class f)) yields Runnable/Callable (core.memoize's arg check).
- AssertionError / Error constructors.

JOLT_FEATURES is gone from the docs: it isn't read anywhere on Chez, and the
reader already includes :clj in its default feature set. RFC 0002's
{:jolt :default} design was reverted in the reader; docs now match the code.

Raises the SCI floor 205 -> 210.
2026-06-25 13:23:05 -04:00
Yogthos
80b2dfa9f9 Soft/WeakReference: real GC eviction via Chez weak pairs + guardians
Replace the strong-ref stub with genuine reclamation. The referent is held
through a weak-cons, so Chez's generational collector reclaims it once it is
otherwise unreachable (the pair's car becomes the bwp object, and .get returns
nil). A guardian registered on the referent makes the reference itself available
the instant its referent is collected, which ReferenceQueue.poll surfaces as
enqueued — the same hook clojure.core.cache's clear-soft-cache! drains.

Chez has no softer-than-weak reference, so a SoftReference clears on
unreachability rather than under memory pressure: a SoftCache evicts more eagerly
than the JVM's but is now real GC eviction, not an unbounded strong cache.
WeakReference gets the same (faithful) semantics. Added System/gc -> a full
collect so callers (and the queue) can force the cycle.

core.cache stays 1314/0/0 (its test values are immortal literals). Corpus row for
System/gc; make test + shakesmoke green.
2026-06-25 11:32:32 -04:00
Yogthos
9312ad0937 Thread/CountDownLatch + SoftReference/ConcurrentHashMap so core.cache fully passes
Closes the last clojure.core.cache gaps (now 1314/0/0, including the 1000-thread
cache-stampede):

- java.lang.Thread over Chez fork-thread (shared heap): (Thread. thunk) + start/
  join/run/isAlive, on a "user-thread" tag distinct from Thread/currentThread's
  interrupt shim. java.util.concurrent.CountDownLatch (count/mutex/condition).
- java.util.concurrent.ConcurrentHashMap = the mutable HashMap shim; get / count /
  contains? read it (clojure.core), which SoftCache uses on its backing map.
- java.lang.ref.SoftReference / ReferenceQueue: no JVM GC reference semantics, so
  the referent is held strongly (a SoftCache is unbounded rather than GC-evicting),
  but enqueue / poll work so clear-soft-cache! drains the queue.

JVM-certified corpus rows. make test + shakesmoke green.
2026-06-25 11:15:12 -04:00
Yogthos
f5455115a0 deftype/record: clojure.lang collection interfaces + protocol identity
Running clojure.core.match (a macro-heavy library that builds its compiler out
of deftypes implementing clojure.lang interfaces) shook out a cluster of general
gaps. Its own suite goes from not-loading to 111/115 assertions.

- deftype/defrecord implementing a clojure.lang collection interface now drives
  the core fns: Indexed -> nth, Counted -> count, Associative -> assoc, ILookup
  -> get/valAt (non-field keys only, so a method's own field bindings don't
  recurse), ISeq -> seq/first/rest, IPersistentCollection -> conj, IFn -> the
  value is callable. A jrec is still a map of fields by default; the interface
  method wins when declared.

- Multi-arity inline methods are grouped into one fn (a type with (nth [_ i]) and
  (nth [_ i x]) kept only the last before). Built as data, not a nested
  syntax-quote, so a `(= ~ocr ~l) method body keeps its unquotes.

- instance?/satisfies? recognize a protocol a type implements, including a MARKER
  protocol with no methods (core.match's IPseudoPattern) — deftype/defrecord now
  record protocol satisfaction even with zero methods. Added ILookup/Indexed/
  Counted to the instance? taxonomy for the built-in collections.

- Syntax-quote: a fully-qualified class name (clojure.lang.ILookup) stays bare
  instead of being namespace-qualified; (unquote x) is detected in a lazy seq
  (a macro that builds its template with map, e.g. deftype's rewrite-set).

- clojure.set union/intersection/difference are variadic (& sets) + union 0-arity.
- java map view methods: keySet/values/entrySet/size/isEmpty.
- deprecated java.util.Date getters (getYear/getMonth/...) + the multi-arg
  (Date. year-1900 month0 date hrs min) constructor.

Seed change (deftype/defrecord macros + clojure.set) -> re-minted; the rest are
runtime. 11 JVM-certified corpus rows; make test + shakesmoke green.
2026-06-25 00:14:19 -04:00
Yogthos
7a343351d6 Make clojure.spec.alpha load and run
Four general gaps, shaken out by loading clojure.spec.alpha:

- Special forms were shadowable by a same-named macro. analyze-list
  macroexpanded before checking special forms, so a ns that redefs def/and/or
  (spec excludes them via :refer-clojure :exclude) made a bare def resolve to
  the macro instead of the special form, breaking every defn after. Now a head
  in the special-form set is never macroexpanded, matching the reference
  macroexpand1 isSpecial check.

- reify dropped all but the last arity of a multi-arity protocol method (spec
  reifies (specize* [s]) and (specize* [s _])). The macro keyed methods by name
  and overwrote; now it groups arities into one multi-arity fn.

- reify instances did not implement IObj: with-meta threw and (instance?
  clojure.lang.IObj r) was false. Every Clojure reify carries metadata. with-meta
  now copies the reify to a fresh identity (shared method table) and keys its
  meta; instance? IObj/IMeta is true for any reify. This was the registry bug —
  spec's with-name returned nil for specs, so get-spec missed.

- (set! (. Class field) val) was rejected. spec toggles
  clojure.lang.RT/checkSpecAsserts this way; the analyzer now lowers it to a
  jolt.host/set-static-field! call over a mutable-statics table, and a plain
  Class/field read consults that table.

Also: .name/.getName on a Namespace and .ns/.sym on a Var (spec's ns-qualify /
->sym). analyzer + reify are seed sources (re-minted). spec.alpha now does
valid?/conform/cat/keys/explain-str/check-asserts. tick.alpha.interval-test still
needs time-literals data readers (separate).
2026-06-24 19:46:22 -04:00
Yogthos
8c7b98e5f2 (class x) returns a JVM-faithful Class value
class / .getClass now return a Class value that renders like the JVM —
(str c)/.toString -> "class <name>", pr -> "<name>", .getName/.getSimpleName/
.getCanonicalName work — but stays = and hash equal to its name string, so
(= (class x) String), class-keyed maps, multimethod dispatch on class, and
instance? keep working against the bare class-name tokens. instance? unwraps
a Class passed as the type arg.

clojure.test/class-match? no longer assumes (class e) is a string (a jolt-ism;
on the JVM a Class isn't a string either) — reads the name via .getName.

Matches JVM Class.toString, which libraries surface in error messages
(clojure.data.json DJSON-54 expects "...of class java.net.URI"). data.json
suite 139/139 bar the one UTF-16 surrogate test (Unicode-scalar char model).

5 corpus rows certified vs JVM; make test + shakesmoke green, 0 new divergences.
2026-06-24 16:46:09 -04:00
Yogthos
f66cf486b0 Port real clojure.pprint (pretty-printer + cl-format)
Replace the 33-line pprint shim with a column-aware pretty printer and a
Common Lisp cl-format engine, ported from the ClojureScript implementation
(no STM — atom-backed fields) and adapted to JVM-Clojure interop. Provides
pprint/write/write-out/with-pprint-dispatch/formatter-out/cl-format and
simple/code dispatch.

core print routes column-aware into an active pretty-writer via a __write
hook (suppressed inside with-out-str captures); PrintWriter host class
forwards into the wrapped writer. Re-mint: pprint is baked into the seed.

Unblocks clojure.data.json/pprint (its pretty-printing test passes).
Directives ~R/~P/~C/~F/~E/~G/~$/~(~) not ported (unused by the targets).

make test + shakesmoke green, 0 new divergences, selfhost holds.
2026-06-24 16:09:09 -04:00
Yogthos
31c8f56784 Host interop for clojure.data.json: readers, numbers, dates, exceptions
Shaking out clojure.data.json's own test suite (now 134/137):

- Reader.read(char[],off,len) bulk read + PushbackReader.unread(char[],off,len)
  on the string/pushback reader jhosts; instance? java.io.PushbackReader/
  Reader/StringReader (data.json re-wraps a reader unless it's already a
  PushbackReader, so this is load-bearing for repeated reads).
- number protocol dispatch by actual type: a flonum is Double (not Long),
  exact ratio is Ratio, exact integer is Long — value-host-tags split.
- Integer/toHexString|toOctalString|toBinaryString|toString; .isNaN/.isInfinite
  as instance methods on numbers.
- EOFException ctor/class; .isArray on a class-name string.
- dispatch tags for the uuid/bigdec/inst host values so a protocol extended to
  java.util.UUID / java.math.BigDecimal / java.util.Date / java.time.Instant
  reaches its impl; canonical-host-tag strips java.math./java.time.
- instant/zoned/local time values compare = by epoch-ms (two parsed Instants).
- java.time.Instant/parse, java.sql.Date ctor + valueOf, TimeZone/getDefault,
  DateTimeFormatter/ISO_INSTANT.

All runtime .ss (no re-mint). 9 corpus rows certified vs JVM; make test +
shakesmoke green, 0 new divergences.
2026-06-24 15:16:02 -04:00
Yogthos
8bd781e6a8 String/char-array interop: getChars, String(char[]), str of StringBuilder, append(csq,start,end)
- String.getChars copies into a char-array; String. builds from a char[]
  (whole or offset/count slice); subSequence returns the substring.
- str of a StringBuilder returns its content (was the opaque host object;
  .toString already worked).
- Appendable.append gains the 3-arg subsequence form append(csq,start,end)
  on StringBuilder/StringWriter/file/port writers.
- reader combines a \uXXXX surrogate pair into the one Unicode scalar
  (😃) instead of crashing on the lone high surrogate; a stray surrogate
  maps to U+FFFD. (A high-plane char re-escaped as \uXXXXX stays the
  irreducible UTF-16/scalar divergence.)
- TimeZone/getDefault.

These let clojure.data.json read and write JSON; its own suite goes from
not loading to 97/133 assertions passing (remainder: per-type writer
dispatch for uuid/bigdec/date, EOFException, niche date interop).
2026-06-24 14:36:13 -04:00
Yogthos
c6b8f31608 instance? recognizes common host interfaces
(instance? clojure.lang.Named :a), java.lang.CharSequence/Number,
java.util.Map/Set/List/Collection, clojure.lang.Associative now report
true for jolt's value model, matching the JVM (a Map is not a Collection;
a List excludes sets/maps). Libraries branch on these — data.json's
default-write-key-fn keys on clojure.lang.Named, so map keys serialized
with the leading colon before this.
2026-06-24 14:21:59 -04:00
Yogthos
b2394325d4 Recognize core clojure.lang interfaces in instance?
(instance? clojure.lang.IObj/IMapEntry/IRecord/IPersistentMap/... x) all returned
false, so libraries branching on these interfaces (e.g. a metadata-preserving
walk guarded by IObj) silently misbehaved. Register an instance-check arm mapping
the interface tokens to jolt predicates, matched by last dotted segment.
2026-06-24 11:03:34 -04:00
Yogthos
289e41943e Shim clojure.lang.LineNumberingPushbackReader
A pushback-reader (drainable, so clojure.edn/read over it works); getLineNumber
is a stub since jolt doesn't track line numbers. Config readers (aero) wrap their
input in one.
2026-06-24 09:13:28 -04:00
Yogthos
a594c9deb4 refactor: rename host-static-{statics,objects}.ss for clarity (jolt-wn0u)
The trio split on a fine axis (registry core / statics / object classes) but the
names didn't say so — 'static-statics'/'static-objects' and headers that read
'Continues X'. Rename:
  host-static-statics.ss -> host-static-methods.ss  (Class/member statics + fields)
  host-static-objects.ss -> host-static-classes.ss  (instantiable object classes)
host-static.ss stays the registry core. Headers rewritten to state each file's role
and what it covers instead of chaining. rt.ss loads + the one comment reference +
MODULES.md updated. No code moved; runtime .ss, make test green.
2026-06-23 23:42:11 -04:00
Renamed from host/chez/host-static-objects.ss (Browse further)