Skip to content

Commit b93cf03

Browse files
authored
Fix :static-methods option for class with different name in host (#972)
1 parent 451fae7 commit b93cf03

File tree

7 files changed

+58
-45
lines changed

7 files changed

+58
-45
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,4 @@ libsci.build_artifacts.txt
4343
src/scratch.clj
4444
src/scratch.cljs
4545
.lein-failures
46+
scratch.clj

src/sci/impl/analyzer.cljc

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,43 +1009,42 @@
10091009
:ns @utils/current-ns
10101010
:file @utils/current-file)]
10111011
#?(:clj (if (class? instance-expr)
1012-
(if (nil? args)
1013-
(if field-access
1014-
(let [method-name (subs method-name 1)]
1015-
(sci.impl.types/->Node
1016-
(interop/get-static-field instance-expr method-name)
1017-
stack))
1018-
;; https://clojure.org/reference/java_interop
1019-
;; If the second operand is a symbol and no args are
1020-
;; supplied it is taken to be a field access - the
1021-
;; name of the field is the name of the symbol, and
1022-
;; the value of the expression is the value of the
1023-
;; field, unless there is a no argument public method
1024-
;; of the same name, in which case it resolves to a
1025-
;; call to the method.
1026-
(if-let [_
1027-
(try (Reflector/getStaticField ^Class instance-expr ^String method-name)
1028-
(catch IllegalArgumentException _ nil))]
1029-
(sci.impl.types/->Node
1030-
(interop/get-static-field instance-expr method-name)
1031-
stack)
1032-
(let [arg-count (count args)
1033-
args (object-array args)]
1012+
(let [static-method
1013+
#(let [arg-count (count args)
1014+
args (object-array args)
1015+
class-expr (:class-expr (meta expr))]
1016+
;; prefab static-methods
1017+
(if-let [f (some-> ctx :env deref
1018+
:class->opts :static-methods
1019+
(get (interop/fully-qualify-class ctx class-expr))
1020+
(get method-expr))]
1021+
(return-call ctx expr f (cons instance-expr args) stack nil)
1022+
(sci.impl.types/->Node
1023+
(interop/invoke-static-method ctx bindings instance-expr method-name
1024+
args arg-count)
1025+
stack)))]
1026+
(if (nil? args)
1027+
(if field-access
1028+
(let [method-name (subs method-name 1)]
1029+
(sci.impl.types/->Node
1030+
(interop/get-static-field instance-expr method-name)
1031+
stack))
1032+
;; https://clojure.org/reference/java_interop
1033+
;; If the second operand is a symbol and no args are
1034+
;; supplied it is taken to be a field access - the
1035+
;; name of the field is the name of the symbol, and
1036+
;; the value of the expression is the value of the
1037+
;; field, unless there is a no argument public method
1038+
;; of the same name, in which case it resolves to a
1039+
;; call to the method.
1040+
(if-let [_
1041+
(try (Reflector/getStaticField ^Class instance-expr ^String method-name)
1042+
(catch IllegalArgumentException _ nil))]
10341043
(sci.impl.types/->Node
1035-
(interop/invoke-static-method ctx bindings instance-expr method-name
1036-
args arg-count)
1037-
stack))))
1038-
(let [arg-count (count args)
1039-
args (object-array args)]
1040-
;; prefab static-methods
1041-
(if-let [f (some-> ctx :env deref
1042-
:class->opts :static-methods
1043-
(get (.getName ^Class instance-expr)) (get method-expr))]
1044-
(return-call ctx expr f (cons instance-expr args) stack nil)
1045-
(sci.impl.types/->Node
1046-
(interop/invoke-static-method ctx bindings instance-expr method-name
1047-
args arg-count)
1048-
stack))))
1044+
(interop/get-static-field instance-expr method-name)
1045+
stack)
1046+
(static-method)))
1047+
(static-method)))
10491048
(let [arg-count #?(:cljs nil :clj (count args))
10501049
args (object-array args)
10511050
#?@(:clj [^"[Ljava.lang.Class;" arg-types (when (and (pos? arg-count))
@@ -1550,8 +1549,9 @@
15501549
f (or fast-path f)]
15511550
(cond (and f-meta (::static-access f-meta))
15521551
#?(:clj
1553-
(expand-dot** ctx (with-meta (list* '. (first f) (second f) (rest expr))
1554-
m))
1552+
(let [[clazz meth class-expr] f]
1553+
(analyze-dot ctx (with-meta (list* '. clazz meth (rest expr))
1554+
(assoc m :class-expr class-expr))))
15551555
:cljs
15561556
(let [[class method-path] f
15571557
last-path (last method-path)

src/sci/impl/interop.cljc

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,16 @@
115115
sym))
116116
(when (contains? class->opts sym)
117117
sym)))
118-
(get (:imports env) sym)
119-
(let [cnn (utils/current-ns-name)]
120-
(get-in env [:namespaces cnn :imports sym])))))
118+
(let [cnn (utils/current-ns-name)
119+
imports (get-in env [:namespaces cnn :imports])]
120+
(if-let [[_ v] (find imports sym)]
121+
;; finding a nil v means the object was unmapped
122+
v
123+
(get-in env [:imports sym]))))))
121124

122125
(defn resolve-class-opts [ctx sym]
126+
;; note, we can't re-use fully-qualify class in this function, although it's
127+
;; almost the same, since `js/Foo` stays fully qualified
123128
(let [env @(:env ctx)
124129
class->opts (:class->opts env)
125130
class-opts (or #?(:clj (get class->opts sym)

src/sci/impl/opts.cljc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@
124124
(if (map? class-opts)
125125
(if-let [sm (:static-methods class-opts)]
126126
(-> (assoc! class->opts sym class-opts)
127-
(assoc! :static-methods (assoc (:static-methods class->opts) (str sym) sm)))
127+
(assoc! :static-methods (assoc (:static-methods class->opts) sym sm)))
128128
(assoc! class->opts sym class-opts))
129129
(assoc! class->opts sym {:class class-opts}))
130130
(rest kvs))

src/sci/impl/resolve.cljc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@
7070
[sym (if (and call? #?(:clj (not (str/starts-with? sym-name-str "."))))
7171
(with-meta
7272
[clazz #?(:clj sym-name
73-
:cljs (.split (str sym-name) "."))]
73+
:cljs (.split (str sym-name) "."))
74+
sym-ns]
7475
#?(:clj
7576
(if (= "new" sym-name-str)
7677
{:sci.impl.analyzer/invoke-constructor true}

test/sci/interop_test.cljc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,11 @@
118118
"(Class/forName \"java.lang.String\")"
119119
{:imports {'Class 'java.lang.Class}
120120
:classes {'java.lang.Class {:class Class
121-
:static-methods {'forName (fn [_Class _forName] :dude)}}}})))))
121+
:static-methods {'forName (fn [_Class _forName] :dude)}}}})))
122+
(is (= :dude (sci/eval-string
123+
"(sci.lang.Var/cloneThreadBindings)"
124+
{:classes {'sci.lang.Var {:class sci.lang.Var
125+
:static-methods {'cloneThreadBindings (fn [_Class] :dude)}}}})))))
122126

123127
#?(:clj
124128
(deftest clojure-1_12-interop-test

test/sci/namespaces_test.cljc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,9 @@
205205
(ns-unmap *ns* 'cake)
206206
(def resolved (resolve 'cake))
207207
(intern *ns* 'cake "bar")
208-
[resolved cake]))))))))
208+
[resolved cake])))))))
209+
(testing "unmapping Class and then fully qualifying it"
210+
(is (= 'user/String (eval* "(ns-unmap *ns* 'String) `String ;;=> user/String")))))
209211

210212
(deftest find-var-test
211213
(is (eval* "(= #'clojure.core/map (find-var 'clojure.core/map))"))

0 commit comments

Comments
 (0)