Skip to content

Commit 9027c02

Browse files
authored
CLJS-3313: Protocol implementations via metadata: ClojureScript behaves differently from Clojure
CLJS-3313: Protocol implementations via metadata: ClojureScript behaves differently from Clojure need to check the metadata implementation first. Fix test expectations, add ticket test case.
1 parent 1aa5666 commit 9027c02

File tree

2 files changed

+22
-13
lines changed

2 files changed

+22
-13
lines changed

src/main/clojure/cljs/core.cljc

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2072,14 +2072,6 @@
20722072
(missing-protocol
20732073
~(core/str psym "." fname) ~fsig))))
20742074

2075-
;; then check protocol fn in metadata (only when protocol is marked with :extend-via-metadata true)
2076-
check
2077-
(core/if-not (:extend-via-metadata opts)
2078-
check
2079-
`(if-let [meta-impl# (-> ~fsig (core/meta) (core/get '~fqn-fname))]
2080-
(meta-impl# ~@sig)
2081-
~check))
2082-
20832075
;; then check protocol on js string,function,array,object (first dynamic check actually executed)
20842076
check
20852077
`(let [x# (if (nil? ~fsig) nil ~fsig)
@@ -2088,9 +2080,10 @@
20882080
(m# ~@sig)
20892081
~check))]
20902082
`(~sig ~check)))
2091-
expand-sig (core/fn [dyn-name slot sig]
2083+
expand-sig (core/fn [fname dyn-name slot sig]
20922084
(core/let [sig (sig->syms sig)
20932085

2086+
fqn-fname (with-meta (fqn fname) {:cljs.analyzer/no-resolve true})
20942087
fsig (first sig)
20952088

20962089
;; check protocol property on object (first check executed)
@@ -2099,7 +2092,15 @@
20992092
;; Property access needed here.
21002093
(not (nil? (. ~fsig ~(with-meta (symbol (core/str "-" slot)) {:protocol-prop true})))))
21012094
(. ~fsig ~slot ~@sig)
2102-
(~dyn-name ~@sig))]
2095+
(~dyn-name ~@sig))
2096+
2097+
;; then check protocol fn in metadata (only when protocol is marked with :extend-via-metadata true)
2098+
check
2099+
(core/if-not (:extend-via-metadata opts)
2100+
check
2101+
`(if-let [meta-impl# (-> ~fsig (core/meta) (core/get '~fqn-fname))]
2102+
(meta-impl# ~@sig)
2103+
~check))]
21032104
`(~sig ~check)))
21042105
psym (core/-> psym
21052106
(vary-meta update-in [:jsdoc] conj "@interface")
@@ -2147,7 +2148,7 @@
21472148
sigs))]
21482149
(defn ~fname
21492150
~@(map (core/fn [sig]
2150-
(expand-sig dyn-name
2151+
(expand-sig fname dyn-name
21512152
(with-meta (symbol (core/str slot "$arity$" (count sig)))
21522153
{:protocol-prop true})
21532154
sig))

src/test/cljs/cljs/core_test.cljs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1770,13 +1770,21 @@
17701770
(deftest test-cljs-2960
17711771
;; protocol impl via metadata
17721772
(is (= 1 (ext-meta-protocol (with-meta {} {`ext-meta-protocol (fn [_] 1)}))))
1773-
;; actual impl before metadata
1774-
(is (= 2 (ext-meta-protocol (with-meta (SomeMetaImpl. 2) {`ext-meta-protocol (fn [_] 1)}))))
1773+
;; metadata before actual impl
1774+
(is (= 1 (ext-meta-protocol (with-meta (SomeMetaImpl. 2) {`ext-meta-protocol (fn [_] 1)}))))
17751775
;; protocol not marked as :extend-via-metadata so fallthrough to no impl
17761776
(is (thrown? js/Error (non-meta-protocol (with-meta {} {`non-meta-protocol (fn [_] 1)}))))
17771777
;; normal impl call just in case
17781778
(is (= 2 (non-meta-protocol (with-meta (SomeMetaImpl. 2) {`non-meta-protocol (fn [_] 1)})))))
17791779

1780+
(extend-type PersistentArrayMap
1781+
ExtMetaProtocol
1782+
(ext-meta-protocol [m] 2))
1783+
1784+
(deftest test-cljs-3313
1785+
(testing "metadata protocol fn takes precedence over direct implementation"
1786+
(= 1 (ext-meta-protocol (with-meta (array-map) {`ext-meta-protocol (fn [_] 1)})))))
1787+
17801788
(deftest test-cljs-3054
17811789
(testing "`into` behaves the same as Clojure"
17821790
(is (nil? (into nil #{})))

0 commit comments

Comments
 (0)