Skip to content

Commit 9f77604

Browse files
authored
CLJS-3464: parents does not walk JavaScript prototype chain (#296)
- implement `bases` return immediate prototype of arg - implement `supers` returns immediate and indirect protoypes of arg - fix hierarchy code to use js-fn? where Clojure used class - add assertions to derive - uncomment some test assertions - do not mutate the root object
1 parent 5a26a08 commit 9f77604

File tree

2 files changed

+62
-22
lines changed

2 files changed

+62
-22
lines changed

src/main/cljs/cljs/core.cljs

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,10 +1483,18 @@
14831483
IMeta
14841484
(-meta [_] nil))
14851485

1486+
(defn- root-obj
1487+
[]
1488+
(->> js/Function
1489+
(.getPrototypeOf js/Object)
1490+
(.getPrototypeOf js/Object)))
1491+
14861492
(extend-type default
14871493
IHash
14881494
(-hash [o]
1489-
(goog/getUid o)))
1495+
(if (identical? o (root-obj))
1496+
0
1497+
(goog/getUid o))))
14901498

14911499
(extend-type symbol
14921500
IHash
@@ -11262,6 +11270,23 @@ reduces them without incurring seq initialization"
1126211270
(defn- swap-global-hierarchy! [f & args]
1126311271
(apply swap! (get-global-hierarchy) f args))
1126411272

11273+
(defn bases
11274+
"Returns the immediate prototype of c"
11275+
[c]
11276+
(when c
11277+
(let [s (.getPrototypeOf js/Object c)]
11278+
(when s
11279+
(list s)))))
11280+
11281+
(defn supers
11282+
"Returns the immediate and indirect prototypes of c, if any"
11283+
[c]
11284+
(loop [ret (set (bases c)) cs ret]
11285+
(if (seq cs)
11286+
(let [c (first cs) bs (bases c)]
11287+
(recur (into ret bs) (into (disj cs c) bs)))
11288+
(not-empty ret))))
11289+
1126511290
(defn ^boolean isa?
1126611291
"Returns true if (= child parent), or child is directly or indirectly derived from
1126711292
parent, either via a JavaScript type inheritance relationship or a
@@ -11270,33 +11295,46 @@ reduces them without incurring seq initialization"
1127011295
hierarchy"
1127111296
([child parent] (isa? @(get-global-hierarchy) child parent))
1127211297
([h child parent]
11273-
(or (= child parent)
11274-
;; (and (class? parent) (class? child)
11275-
;; (. ^Class parent isAssignableFrom child))
11276-
(contains? ((:ancestors h) child) parent)
11277-
;;(and (class? child) (some #(contains? ((:ancestors h) %) parent) (supers child)))
11278-
(and (vector? parent) (vector? child)
11279-
(== (count parent) (count child))
11280-
(loop [ret true i 0]
11281-
(if (or (not ret) (== i (count parent)))
11282-
ret
11283-
(recur (isa? h (child i) (parent i)) (inc i))))))))
11298+
(or (= child parent)
11299+
(and (js-fn? parent) (js-fn? child)
11300+
(instance? parent child))
11301+
(contains? ((:ancestors h) child) parent)
11302+
(and (js-fn? child) (some #(contains? ((:ancestors h) %) parent) (supers child)))
11303+
(and (vector? parent) (vector? child)
11304+
(== (count parent) (count child))
11305+
(loop [ret true i 0]
11306+
(if (or (not ret) (== i (count parent)))
11307+
ret
11308+
(recur (isa? h (child i) (parent i)) (inc i))))))))
1128411309

1128511310
(defn parents
1128611311
"Returns the immediate parents of tag, either via a JavaScript type
1128711312
inheritance relationship or a relationship established via derive. h
1128811313
must be a hierarchy obtained from make-hierarchy, if not supplied
1128911314
defaults to the global hierarchy"
1129011315
([tag] (parents @(get-global-hierarchy) tag))
11291-
([h tag] (not-empty (get (:parents h) tag))))
11316+
([h tag]
11317+
(not-empty
11318+
(let [tp (get (:parents h) tag)]
11319+
(if (js-fn? tag)
11320+
(into (set (bases tag)) tp)
11321+
tp)))))
1129211322

1129311323
(defn ancestors
1129411324
"Returns the immediate and indirect parents of tag, either via a JavaScript type
1129511325
inheritance relationship or a relationship established via derive. h
1129611326
must be a hierarchy obtained from make-hierarchy, if not supplied
1129711327
defaults to the global hierarchy"
1129811328
([tag] (ancestors @(get-global-hierarchy) tag))
11299-
([h tag] (not-empty (get (:ancestors h) tag))))
11329+
([h tag]
11330+
(not-empty
11331+
(let [ta (get (:ancestors h) tag)]
11332+
(if (js-fn? tag)
11333+
(let [superclasses (set (supers tag))]
11334+
(reduce into superclasses
11335+
(cons ta
11336+
(map #(get (:ancestors h) %) superclasses))))
11337+
ta)))))
1130011338

1130111339
(defn descendants
1130211340
"Returns the immediate and indirect children of tag, through a
@@ -11305,7 +11343,10 @@ reduces them without incurring seq initialization"
1130511343
hierarchy. Note: does not work on JavaScript type inheritance
1130611344
relationships."
1130711345
([tag] (descendants @(get-global-hierarchy) tag))
11308-
([h tag] (not-empty (get (:descendants h) tag))))
11346+
([h tag]
11347+
(if (js-fn? tag)
11348+
(throw (js/Error. "Can't get descendants of constructors"))
11349+
(not-empty (get (:descendants h) tag)))))
1130911350

1131011351
(defn derive
1131111352
"Establishes a parent/child relationship between parent and
@@ -11315,13 +11356,12 @@ reduces them without incurring seq initialization"
1131511356
supplied defaults to, and modifies, the global hierarchy."
1131611357
([tag parent]
1131711358
(assert (namespace parent))
11318-
;; (assert (or (class? tag) (and (instance? cljs.core.Named tag) (namespace tag))))
11359+
(assert (or (js-fn? tag) (and (implements? INamed tag) (namespace tag))))
1131911360
(swap-global-hierarchy! derive tag parent) nil)
1132011361
([h tag parent]
1132111362
(assert (not= tag parent))
11322-
;; (assert (or (class? tag) (instance? clojure.lang.Named tag)))
11323-
;; (assert (instance? clojure.lang.INamed tag))
11324-
;; (assert (instance? clojure.lang.INamed parent))
11363+
(assert (or (js-fn? tag) (implements? INamed tag)))
11364+
(assert (implements? INamed parent))
1132511365
(let [tp (:parents h)
1132611366
td (:descendants h)
1132711367
ta (:ancestors h)

src/test/cljs/cljs/core_test.cljs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,12 @@
197197
(is (= #{:cljs.core-test/rect :cljs.core-test/square} (descendants ::shape)))
198198
(is (true? (isa? 42 42)))
199199
(is (true? (isa? ::square ::shape)))
200-
;(derive ObjMap ::collection)
200+
(derive ObjMap ::collection)
201201
(derive cljs.core.PersistentHashSet ::collection)
202-
;(is (true? (isa? ObjMap ::collection)))
202+
(is (true? (isa? ObjMap ::collection)))
203203
(is (true? (isa? cljs.core.PersistentHashSet ::collection)))
204204
(is (false? (isa? cljs.core.IndexedSeq ::collection)))
205-
;; ?? (isa? String Object)
205+
(isa? js/String js/Object)
206206
(is (true? (isa? [::square ::rect] [::shape ::shape])))
207207
;; ?? (ancestors java.util.ArrayList)
208208
;; ?? isa? based dispatch tests

0 commit comments

Comments
 (0)