Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #925: support new Clojure 1.12 array notation: String/1, byte/2 #941

Merged
merged 4 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading