spec: rows for every untested var (131 -> 0); the probes found five real bugs

test/spec/untested-vars-spec.janet adds 143 rows asserting jolt's documented
behavior for the whole implemented-untested category — primed arithmetic,
the array/aset/coercion stubs, unchecked-*, the chunk family, JVM-shape
stubs (class/bean/proxy/memfn as resolve-only or :throws), ns/REPL
machinery, and the misc seqs. tools/spec_coverage.py now checks each var as
a whole TOKEN in the test sources (call-position-only matching missed *1,
+', ., .., /, and bare transducer refs like cat).

Writing rows from probed truth surfaced five real bugs, all fixed:
- comp with a jolt-IFn stage silently returned nil ((comp seq :content)) —
  raw Janet keyword application is not jolt invoke. comp is the canonical
  overlay defn now (fixed-arity composed fn, so the hot 1-arg path is two
  direct calls); the seed keeps a private td-comp only for the transducer
  machinery. hof bench +9% vs native, the price of correct IFn dispatch.
- extend (the fn) was a nil-expanding stub MACRO shadowing any definition;
  it's a real fn over register-method now, and extends? (a constant-false
  stub) is real over extenders
- (.. x f g) hit the 'ClassName.' constructor branch (a name ending in a
  dot) and died; .. is the canonical threading macro now
- aclone errored on pvecs; ns-interns/ns-imports returned live host tables
  that count/seq reject (now structs)

Thread/sleep + Thread/yield land as Thread statics beside Math/: sleep parks
the WORKER's own event loop (each future thread has one), which makes timed
deref provably fire — futures-spec gains the timeout-fires, sleep-in-body,
and timed-out-future-still-completes rows. The futures impl itself already
ran on real OS threads (ev/spawn-thread + marshalled results); jolt-ejx was
stale.

Dashboard: implemented+tested 433 -> 564 of 694; implemented-untested and
missing-portable are both EMPTY. Gate: jpm exit 0, all tests passed.
This commit is contained in:
Yogthos 2026-06-10 17:52:30 -04:00
parent daab2ab4cc
commit d06b3fe636
8 changed files with 414 additions and 151 deletions

View file

@ -821,3 +821,20 @@
;; The raw Inst protocol method; jolt insts have one representation, so it is
;; inst-ms itself.
(defn inst-ms* [i] (inst-ms i))
;; Canonical comp — here rather than the seed so each stage is invoked with
;; jolt call semantics: (comp seq :content) works because the keyword stage
;; goes through IFn dispatch (raw Janet keyword application does not).
(defn comp
([] identity)
([f] f)
([f g]
;; fixed arities first (Clojure's own shape): the 1-arg path — every
;; map/filter stage — is two direct calls, no rest-seq, no apply.
(fn
([] (f (g)))
([x] (f (g x)))
([x y] (f (g x y)))
([x y z] (f (g x y z)))
([x y z & args] (f (apply g x y z args)))))
([f g & fs] (reduce comp (comp f g) fs)))

View file

@ -295,6 +295,43 @@
(protocol-dispatch ~(name pname) ~(name (first sig)) this# rest#))))
sigs))))
;; Member threading: (.. x f g) => (. (. x f) g); a parenthesized member
;; carries args. Canonical Clojure shape, single-arity defmacro.
(defmacro .. [x form & more]
(let [step (if (seq? form)
`(. ~x ~(first form) ~@(rest form))
`(. ~x ~form))]
(if (seq more)
`(.. ~step ~@more)
step)))
;; True when atype's methods were registered for this protocol (via extend /
;; extend-type). Tags are canonical host names or ns-qualified record names,
;; so a bare record name also matches its "ns.Name" tag.
(defn extends? [protocol atype]
(let [want (name atype)
dotted (str "." want)
dlen (count dotted)]
(boolean (some (fn [t]
(let [tn (name t)]
(or (= tn want)
(and (> (count tn) dlen)
(= (subs tn (- (count tn) dlen)) dotted)))))
(extenders protocol)))))
;; extend, the FUNCTION (extend-type's runtime sibling): protocol + method-map
;; pairs, methods registered under the type's (canonicalized) name — so
;; (extend 'String P {:m (fn [x] ...)}) dispatches exactly like extend-type.
(defn extend [atype & proto+mmaps]
(loop [s (seq proto+mmaps)]
(when s
(let [proto (first s)
mmap (second s)
pname (name (get proto :name))]
(doseq [[k f] mmap]
(register-method (name atype) pname (name k) f)))
(recur (nnext s)))))
(defmacro extend-type [tsym psym & impls]
;; register-method is a fn (clojure.core); pass type/protocol/method NAMES as
;; strings (not the symbols) so the call compiles as a plain invoke.
@ -308,7 +345,7 @@
(group-by-head type-impls))))
;; extend (the fn form) is not supported — stub to nil, as before.
(defmacro extend [& args] nil)
;; extend is a real FUNCTION now — defined above extend-type.
;; JVM proxies are unsupported.
(defmacro proxy [& args] nil)
;; definterface is JVM-only; bind the name to an empty marker.