Skip to content

Commit

Permalink
Fix #925: support new Clojure 1.12 array notation: String/1, `byte/…
Browse files Browse the repository at this point in the history
…2` (#941)
  • Loading branch information
borkdude authored Oct 10, 2024
1 parent 5d33aaa commit feb973a
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 23 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ SCI is used in [babashka](https://github.com/babashka/babashka),
## Unreleased

- Fix [#917](https://github.com/babashka/sci/issues/917): support new Clojure 1.12 Java interop: `String/new`, `String/.length` and `Integer/parseInt` as fns
- Fix [#925](https://github.com/babashka/sci/issues/925): support new Clojure 1.12 array notation: `String/1`, `byte/2`
- Fix [#926](https://github.com/babashka/sci/issues/926): Support `add-watch` on vars in CLJS
- Support `aset` on primitive array using reflection
- Fix [#928](https://github.com/babashka/sci/issues/928): record constructor supports optional meta + ext map
Expand Down
10 changes: 5 additions & 5 deletions src/sci/impl/analyzer.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -1802,14 +1802,14 @@
nil))))))

#?(:clj
(defn analyze-interop-ifn [_ctx expr [^Class clazz meth]]
(defn analyze-interop [_ctx expr [^Class clazz meth]]
(let [meth (str meth)
stack (assoc (meta expr)
:ns @utils/current-ns
:file @utils/current-file)]
(if-let [_fld (try (Reflector/getStaticField ^Class clazz ^String meth)
(catch IllegalArgumentException _
nil))]
(catch IllegalArgumentException _
nil))]
(sci.impl.types/->Node
(interop/get-static-field clazz meth)
stack)
Expand Down Expand Up @@ -1866,8 +1866,8 @@
(sci.impl.types/->Node
(faster/deref-1 v)
nil))))
(:sci.impl.analyzer/interop-ifn mv)
(analyze-interop-ifn ctx expr v)
(:sci.impl.analyzer/interop mv)
(analyze-interop ctx expr v)
:else v))
;; don't evaluate records, this check needs to go before map?
;; since a record is also a map
Expand Down
32 changes: 32 additions & 0 deletions src/sci/impl/interop.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,35 @@

(defn resolve-class [ctx sym]
(:class (resolve-class-opts ctx sym)))

#?(:clj
(def prim->class
{'int Integer/TYPE
'ints (Class/forName "[I")
'long Long/TYPE
'longs (Class/forName "[J")
'float Float/TYPE
'floats (Class/forName "[F")
'double Double/TYPE
'doubles (Class/forName "[D")
'void Void/TYPE
'short Short/TYPE
'shorts (Class/forName "[S")
'boolean Boolean/TYPE
'booleans (Class/forName "[Z")
'byte Byte/TYPE
'bytes (Class/forName "[B")
'char Character/TYPE
'chars (Class/forName "[C")}))

#?(:clj
(def ->array-class
(memoize (fn [clazz dim]
(class (apply make-array clazz (vec (repeat dim 0))))))))

#?(:clj
(defn resolve-array-class [ctx sym-ns ^String sym-name-str]
(when-let [clazz (or (resolve-class ctx sym-ns)
(get prim->class sym-ns))]
(let [dim (- (int (.charAt sym-name-str 0)) 48)]
(->array-class clazz dim)))))
37 changes: 21 additions & 16 deletions src/sci/impl/parser.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
(:refer-clojure :exclude [read-string eval])
(:require
[clojure.string :as str]
[clojure.tools.reader.reader-types :as r]
[clojure.tools.reader.reader-types :as rt]
[edamame.core :as edamame]
[sci.impl.interop :as interop]
[sci.impl.types :as types]
[sci.impl.utils :as utils]
[clojure.tools.reader.reader-types :as rt]))
[sci.impl.utils :as utils]))

#?(:clj (set! *warn-on-reflection* true))

Expand Down Expand Up @@ -94,17 +93,23 @@
(symbol current-ns-str sym-name-str)))))
ret (if-not sym-ns
(res-without-sym sym)
(let [nss (get env :namespaces)]
(if (get nss sym-ns)
sym
(if-let [ns (get aliases sym-ns)]
(symbol (str ns) (name sym))
#?(:cljs
;; This enables using `(fs/readFileSync) mode in macros, e.g. in nbb
(if-let [import (-> nss (get current-ns) :imports (get sym-ns))]
(symbol (str import) (name sym))
sym)
:clj sym)))))]
(let [sym-name (name sym)]
(or
#?(:clj (when (and (= 1 (.length sym-name))
(Character/isDigit (.charAt sym-name 0)))
(when-let [clazz ^Class (interop/resolve-array-class ctx sym-ns sym-name)]
(symbol (pr-str clazz)))))
(let [nss (get env :namespaces)]
(if (get nss sym-ns)
sym
(if-let [ns (get aliases sym-ns)]
(symbol (str ns) sym-name)
#?(:cljs
;; This enables using `(fs/readFileSync) mode in macros, e.g. in nbb
(if-let [import (-> nss (get current-ns) :imports (get sym-ns))]
(symbol (str import) (name sym))
sym)
:clj sym)))))))]
ret))

(defn throw-eval-read [_]
Expand All @@ -122,10 +127,10 @@
auto-resolve)))

(defn get-line-number [reader]
(r/get-line-number reader))
(rt/get-line-number reader))

(defn get-column-number [reader]
(r/get-column-number reader))
(rt/get-column-number reader))

(defn parse-next
([ctx r]
Expand Down
10 changes: 8 additions & 2 deletions src/sci/impl/resolve.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
([ctx sym call?] (lookup* ctx sym call? false))
([ctx sym call? only-var?]
(let [sym-ns (some-> (namespace sym) symbol)
sym-name (symbol (name sym))
sym-name-str (name sym)
sym-name (symbol sym-name-str)
env (faster/get-2 ctx :env)
env @env
cnn (utils/current-ns-name)
Expand All @@ -51,6 +52,11 @@
sym-ns (get (:ns-aliases env) sym-ns sym-ns)]
(if sym-ns
(or
#?(:clj
(when (and (= 1 (.length sym-name-str))
(Character/isDigit (.charAt sym-name-str 0)))
(when-let [clazz (interop/resolve-array-class ctx sym-ns sym-name-str)]
[sym clazz])))
(when
#?(:clj (= 'clojure.core sym-ns)
:cljs (or (= 'clojure.core sym-ns)
Expand All @@ -69,7 +75,7 @@
#?(:clj
(with-meta
[clazz sym-name]
{:sci.impl.analyzer/interop-ifn true})
{:sci.impl.analyzer/interop true})
:cljs
(let [stack (assoc (meta sym)
:file @utils/current-file
Expand Down
23 changes: 23 additions & 0 deletions test/sci/interop_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,29 @@
(is (= [1 2 3] (eval* "(map String/.length [\"1\" \"22\" \"333\"])")))
(is (= ["1" "22" "333"] (eval* "(map String/new [\"1\" \"22\" \"333\"])")))))

#?(:clj
(when-not tu/native?
(deftest clojure-1_12-array-test
(let [byte-1 (class (make-array Byte/TYPE 0))
byte-3 (class (make-array Byte/TYPE 0 0 0))
String-1 (class (make-array String 0))]
(is (= (class (make-array Long/TYPE 0)) (eval* "long/1")))
(is (= (class (make-array Long/TYPE 0 0)) (eval* "long/2") ))
(is (= (class (make-array Integer/TYPE 0)) (eval* "int/1")))
(is (= (class (make-array Double/TYPE 0)) (eval* "double/1") ))
(is (= (class (make-array Short/TYPE 0)) (eval* "short/1") ))
(is (= (class (make-array Boolean/TYPE 0)) (eval* "boolean/1")))
(is (= byte-1 (eval* "byte/1")))
(is (= (class (make-array Float/TYPE 0)) (eval* "float/1")))
(is (= (class (make-array String 0)) (eval* "String/1")))
(is (= String-1 (eval* "java.lang.String/1")))
(is (= (symbol (pr-str byte-1)) (eval* "`byte/1")))
(is (= (symbol (pr-str byte-3)) (eval* "`byte/3")))
(is (= (symbol "java.util.UUID/1") (eval* "`java.util.UUID/1")))
(is (= (symbol (pr-str String-1)) (eval* "`String/1")))
(is (= (symbol (pr-str String-1)) (eval* "`java.lang.String/1")))
(is (= [(symbol "long/2")] (eval* "['long/2]") (eval* "`[~'long/2]")))))))

(when-not tu/native?
(deftest exception-data
(testing "top-level interop forms have line and column data"
Expand Down

0 comments on commit feb973a

Please sign in to comment.