|
263 | 263 | (find-method-any-protocol (jrec-tag v) "toString") |
264 | 264 | (record-method-dispatch v "toString" jolt-nil)))) |
265 | 265 |
|
| 266 | +;; the interfaces every defrecord carries, spelled both ways (the record arm of |
| 267 | +;; value-host-tags below). |
| 268 | +(define jrec-record-iface-tags |
| 269 | + '("IRecord" "clojure.lang.IRecord" "IPersistentMap" "clojure.lang.IPersistentMap" |
| 270 | + "APersistentMap" "Associative" "ILookup" "Seqable" "Counted" |
| 271 | + "IPersistentCollection" "IObj" "IMeta" "Map" "java.util.Map" |
| 272 | + "Iterable" "java.lang.Iterable" "Object")) |
| 273 | + |
| 274 | +;; a jch-tags list always ends with "Object"; the tail without it, for splicing |
| 275 | +;; ahead of another tag list. |
| 276 | +(define (jch-tags-sans-object ts) |
| 277 | + (if (null? (cdr ts)) '() (cons (car ts) (jch-tags-sans-object (cdr ts))))) |
| 278 | + |
| 279 | +;; A reify's dispatch tags: each declared protocol/interface as its JVM interface |
| 280 | +;; name plus that interface's modeled ancestry, deduped, Object last — so an |
| 281 | +;; extend-protocol filed under an interface name (clojure.lang.IReduceInit, |
| 282 | +;; java.lang.Iterable, …) reaches a reify declaring it, exactly as instanceof |
| 283 | +;; answers on the JVM. The reify's own method table is consulted before these |
| 284 | +;; (protocol-resolve), so an inline impl still wins. |
| 285 | +(define (jreify-host-tags obj) |
| 286 | + (let loop ((ps (jreify-protos obj)) (acc '())) |
| 287 | + (if (null? ps) |
| 288 | + (reverse (cons "Object" acc)) |
| 289 | + (let inner ((ts (jch-tags (proto-iface-name (car ps)))) (acc acc)) |
| 290 | + (if (null? ts) |
| 291 | + (loop (cdr ps) acc) |
| 292 | + (inner (cdr ts) |
| 293 | + (let ((t (car ts))) |
| 294 | + (if (or (string=? t "Object") (member t acc)) acc (cons t acc))))))))) |
| 295 | + |
266 | 296 | ;; host type-tag candidates for a non-record value (extend-protocol on builtins). |
267 | 297 | (define (value-host-tags obj) |
268 | 298 | ;; numbers dispatch by actual type (a Double is NOT a Long): flonum -> Double, |
|
364 | 394 | ;; IWalkTerm to clojure.lang.IRecord, and walking a record value must hit |
365 | 395 | ;; that, not the Object default (which would recur forever). The record's |
366 | 396 | ;; own type is tried first (dispatch checks jrec-tag before these tags). |
| 397 | + ;; a reify reports its declared interfaces and their ancestry — see |
| 398 | + ;; jreify-host-tags above. |
| 399 | + ((jreify? obj) (jreify-host-tags obj)) |
| 400 | + ;; A record that declares FURTHER interfaces beyond the automatic map set |
| 401 | + ;; reports those too (register-inline-protocol! files them as the tag's |
| 402 | + ;; supers in the class graph, so cddr of the cached jch-tags list — own |
| 403 | + ;; tag and simple segment dropped — is exactly the declared ancestry). |
| 404 | + ;; The common record declares nothing extra and keeps the shared list, |
| 405 | + ;; costing this hot fallback path one cons, as before. |
367 | 406 | ((jrec-record? obj) |
368 | | - (cons (jrec-tag obj) |
369 | | - '("IRecord" "clojure.lang.IRecord" "IPersistentMap" "clojure.lang.IPersistentMap" |
370 | | - "APersistentMap" "Associative" "ILookup" "Seqable" "Counted" |
371 | | - "IPersistentCollection" "IObj" "IMeta" "Map" "java.util.Map" |
372 | | - "Iterable" "java.lang.Iterable" "Object"))) |
373 | | - ;; a bare deftype is opaque — its declared interfaces dispatch via the |
374 | | - ;; inline methods registered under its own tag (tried before these tags). |
375 | | - ((jrec? obj) (list (jrec-tag obj) "Object")) |
| 407 | + (let* ((tag (jrec-tag obj)) |
| 408 | + (extra (cddr (jch-tags tag)))) |
| 409 | + (cons tag (if (null? (cdr extra)) |
| 410 | + jrec-record-iface-tags |
| 411 | + (append (jch-tags-sans-object extra) jrec-record-iface-tags))))) |
| 412 | + ;; a bare deftype dispatches through its own tag first, then its declared |
| 413 | + ;; interfaces and their modeled ancestry — an extend-protocol filed under |
| 414 | + ;; clojure.lang.IReduceInit reaches a deftype declaring it, as instanceof |
| 415 | + ;; does. The tag's own SIMPLE segment is dropped (cddr): extensions on a |
| 416 | + ;; deftype name file under its ns-qualified tag, and a bare segment could |
| 417 | + ;; collide with a host class of the same name. A type declaring no |
| 418 | + ;; interfaces keeps (tag "Object"). |
| 419 | + ((jrec? obj) (let ((tag (jrec-tag obj))) (cons tag (cddr (jch-tags tag))))) |
376 | 420 | ;; a throwable reports its OWN class and that class's ancestry, so |
377 | 421 | ;; (extend-protocol P Throwable …) reaches an ex-info or a host-constructed |
378 | 422 | ;; RuntimeException. clojure.datafy extends Datafiable to Throwable exactly |
|
0 commit comments