From f25f3e297817803528e6c0ea46242c45a8d8e6fe Mon Sep 17 00:00:00 2001
From: Alex Miller This method attempts to find resue the given array as the basis for an array map as quickly as possible. If a trailing element exists in the array or it contains duplicate keys then it delegates to the complex path.
It will reallocate a smaller init array if duplicate keys are found.
+ * + *If a trailing element is found then will attempt to add it to the resulting map as if by conj.
+ *No guarantees about the order of the keys in the trailing element are made.
+ **/ +private static PersistentArrayMap createAsIfByAssocComplexPath(Object[] init, boolean hasTrailing){ + if(hasTrailing) + { + IPersistentCollection trailing = PersistentArrayMap.EMPTY.cons(init[init.length-1]); + init = growSeedArray(init, trailing); + } + // If this looks like it is doing busy-work, it is because it // is achieving these goals: O(n^2) run time like // createWithCheck(), never modify init arg, and only diff --git a/test/clojure/test_clojure/data_structures.clj b/test/clojure/test_clojure/data_structures.clj index 69baf137c6..854560466b 100644 --- a/test/clojure/test_clojure/data_structures.clj +++ b/test/clojure/test_clojure/data_structures.clj @@ -1330,3 +1330,34 @@ (is (= (hash (->Rec 1 1)) (hash (assoc r :a 1)))) (is (= (hash (->Rec 1 1)) (hash (dissoc r2 :c)))) (is (= (hash (->Rec 1 1)) (hash (dissoc (assoc r :c 1) :c)))))) + +(deftest singleton-map-in-destructure-context + (let [sample-map {:a 1 :b 2} + {:keys [a] :as m1} (list sample-map)] + (is (= m1 sample-map)) + (is (= a 1)))) + +(deftest trailing-map-destructuring + (let [sample-map {:a 1 :b 2} + add (fn [& {:keys [a b]}] (+ a b)) + addn (fn [n & {:keys [a b]}] (+ n a b))] + (testing "that kwargs are applied properly given a map in place of the key/val pairs" + (is (= 3 (add :a 1 :b 2))) + (is (= 3 (add {:a 1 :b 2}))) + (is (= 13 (addn 10 :a 1 :b 2))) + (is (= 13 (addn 10 {:a 1 :b 2}))) + (is (= 103 ((partial addn 100) :a 1 {:b 2}))) + (is (= 103 ((partial addn 100 :a 1) {:b 2}))) + (is (= 107 ((partial addn 100 :a 1) {:a 5 :b 2})))) + (testing "built maps" + (let [{:as m1} (list :a 1 :b 2) + {:as m2} (list :a 1 :b 2 {:c 3}) + {:as m3} (list :a 1 :b 2 {:a 0}) + {:keys [a4] :as m4} (list nil)] + (= m1 {:a 1 :b 2}) + (= m2 {:a 1 :b 2 :c 3}) + (= m3 {:a 0 :b 2}) + (= m1 (seq-to-map-for-destructuring (list :a 1 :b 2))) + (= m2 (seq-to-map-for-destructuring (list :a 1 :b 2 {:c 3}))) + (= m3 (seq-to-map-for-destructuring (list :a 1 :b 2 {:a 0}))) + (= a4 nil))))) From 9249412dbd7e761670c4f05b399a10460230707d Mon Sep 17 00:00:00 2001 From: Alex MillerIFns can be passed to higher order functions, e.g. the
- * example below passes plus to read:
inc to map:
*
*
* IFn map = Clojure.var("clojure.core", "map");
diff --git a/src/jvm/clojure/java/api/package.html b/src/jvm/clojure/java/api/package.html
index 6536c33ab5..a24d3c9b79 100644
--- a/src/jvm/clojure/java/api/package.html
+++ b/src/jvm/clojure/java/api/package.html
@@ -57,7 +57,7 @@
IFns can be passed to higher order functions, e.g. the
- example below passes plus to read:
+ example below passes inc to map:
IFn map = Clojure.var("clojure.core", "map");
IFn inc = Clojure.var("clojure.core", "inc");
From ade22645ba5dbf4c0d8115b19938af96d6fb4cd5 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 18 Nov 2021 09:47:56 -0600
Subject: [PATCH 070/285] CLJ-1360 Update clojure.string/split docstring
regarding trailing empty parts
Signed-off-by: Alex Miller
---
src/clj/clojure/string.clj | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/clj/clojure/string.clj b/src/clj/clojure/string.clj
index 35e0650f65..38f0d6d91d 100644
--- a/src/clj/clojure/string.clj
+++ b/src/clj/clojure/string.clj
@@ -218,7 +218,8 @@ Design notes for clojure.string:
(defn split
"Splits string on a regular expression. Optional argument limit is
- the maximum number of splits. Not lazy. Returns vector of the splits."
+ the maximum number of parts. Not lazy. Returns vector of the parts.
+ Trailing empty strings are not returned - pass limit of -1 to return all."
{:added "1.2"}
([^CharSequence s ^Pattern re]
(LazilyPersistentVector/createOwning (.split re s)))
@@ -226,7 +227,7 @@ Design notes for clojure.string:
(LazilyPersistentVector/createOwning (.split re s limit))))
(defn split-lines
- "Splits s on \\n or \\r\\n."
+ "Splits s on \\n or \\r\\n. Trailing empty lines are not returned."
{:added "1.2"}
[^CharSequence s]
(split s #"\r?\n"))
From 554090e7fd5f33fc3c1c962bb34af53a01332a75 Mon Sep 17 00:00:00 2001
From: Arne Brasseur
Date: Thu, 18 Nov 2021 09:41:29 -0600
Subject: [PATCH 071/285] CLJ-2249 Clarify clojure.core/get docstring regarding
sets, strings, arrays, ILookup
Signed-off-by: Alex Miller
---
src/clj/clojure/core.clj | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index a3a32522b4..9f3cdf371f 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -1494,7 +1494,8 @@
[coll key] (. clojure.lang.RT (contains coll key)))
(defn get
- "Returns the value mapped to key, not-found or nil if key not present."
+ "Returns the value mapped to key, not-found or nil if key not present
+ in associative collection, set, string, array, or ILookup instance."
{:inline (fn [m k & nf] `(. clojure.lang.RT (get ~m ~k ~@nf)))
:inline-arities #{2 3}
:added "1.0"}
From e1220832d900745727cf5a1b69f4a6cd664f1443 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Tue, 5 Oct 2021 10:32:45 -0500
Subject: [PATCH 072/285] CLJ-2488 Add definition to reify docstring
Signed-off-by: Alex Miller
---
src/clj/clojure/core_deftype.clj | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/clj/clojure/core_deftype.clj b/src/clj/clojure/core_deftype.clj
index 786f0d4b53..7906ccbe35 100644
--- a/src/clj/clojure/core_deftype.clj
+++ b/src/clj/clojure/core_deftype.clj
@@ -67,8 +67,9 @@
(throw (IllegalArgumentException. (apply print-str "Unsupported option(s) -" bad-opts))))
[interfaces methods opts]))
-(defmacro reify
- "reify is a macro with the following structure:
+(defmacro reify
+ "reify creates an object implementing a protocol or interface.
+ reify is a macro with the following structure:
(reify options* specs*)
From 631c46ed98ed3bfefdb8a15080e004ab470b0bf4 Mon Sep 17 00:00:00 2001
From: Nikita Prokopov
Date: Sun, 30 Aug 2015 18:56:04 +0600
Subject: [PATCH 073/285] CLJ-1808 map-invert should use reduce-kv and
transient
Signed-off-by: Alex Miller
---
src/clj/clojure/set.clj | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/clj/clojure/set.clj b/src/clj/clojure/set.clj
index b63a004475..9ffb72900a 100644
--- a/src/clj/clojure/set.clj
+++ b/src/clj/clojure/set.clj
@@ -106,7 +106,11 @@
(defn map-invert
"Returns the map with the vals mapped to the keys."
{:added "1.0"}
- [m] (reduce (fn [m [k v]] (assoc m v k)) {} m))
+ [m]
+ (persistent!
+ (reduce-kv (fn [m k v] (assoc! m v k))
+ (transient {})
+ m)))
(defn join
"When passed 2 rels, returns the rel corresponding to the natural
From d004e799364e29815dc1b080dbf9e8bd9858c321 Mon Sep 17 00:00:00 2001
From: Steve Miner
Date: Tue, 5 Oct 2021 09:48:39 -0500
Subject: [PATCH 074/285] CLJ-2065 Support IKVReduce on SubVector
Signed-off-by: Alex Miller
---
src/jvm/clojure/lang/APersistentVector.java | 12 +++++++++++-
test/clojure/test_clojure/vectors.clj | 4 ++++
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/src/jvm/clojure/lang/APersistentVector.java b/src/jvm/clojure/lang/APersistentVector.java
index c9f15cdd3f..de8a09ea68 100644
--- a/src/jvm/clojure/lang/APersistentVector.java
+++ b/src/jvm/clojure/lang/APersistentVector.java
@@ -544,7 +544,7 @@ public APersistentVector.RSeq withMeta(IPersistentMap meta){
}
}
-public static class SubVector extends APersistentVector implements IObj{
+public static class SubVector extends APersistentVector implements IObj, IKVReduce{
public final IPersistentVector v;
public final int start;
public final int end;
@@ -574,6 +574,16 @@ public Iterator iterator(){
return super.iterator();
}
+ public Object kvreduce(IFn f, Object init){
+ int cnt = count();
+ for (int i=0; i= end) || (i < 0))
throw new IndexOutOfBoundsException();
diff --git a/test/clojure/test_clojure/vectors.clj b/test/clojure/test_clojure/vectors.clj
index 0bea3ff4b8..0be0bf14e8 100644
--- a/test/clojure/test_clojure/vectors.clj
+++ b/test/clojure/test_clojure/vectors.clj
@@ -416,3 +416,7 @@
(is (= [0 1 2 3] (vec (reify clojure.lang.IReduceInit
(reduce [_ f start]
(reduce f start (range 4))))))))
+
+(deftest test-reduce-kv-vectors
+ (is (= 25 (reduce-kv + 10 [2 4 6])))
+ (is (= 25 (reduce-kv + 10 (subvec [0 2 4 6] 1)))))
From e21bbfe1d1b06ae703e939951e6c8f199969d70b Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Tue, 23 Nov 2021 12:47:24 -0600
Subject: [PATCH 075/285] CLJ-2326 Add :direct-linking to *compiler-options*
docstring and remove Alpha marking
Signed-off-by: Alex Miller
---
src/clj/clojure/core.clj | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 9f3cdf371f..0dba6fcd97 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -6473,7 +6473,10 @@ fails, attempts to require sym's namespace and retries."
Supported options:
:elide-meta - a collection of metadata keys to elide during compilation.
:disable-locals-clearing - set to true to disable clearing, useful for using a debugger
- Alpha, subject to change."
+ :direct-linking - set to true to use direct static invocation of functions, rather than vars
+ Note that call sites compiled with direct linking will not be affected by var redefinition.
+ Use ^:redef (or ^:dynamic) on a var to prevent direct linking and allow redefinition.
+ See https://clojure.org/reference/compilation for more information."
{:added "1.4"})
(add-doc-and-meta *ns*
From 8ebad0ab3f912932d94874120cad89493f2aa22e Mon Sep 17 00:00:00 2001
From: "Hudson @ build.clojure.org"
Date: Tue, 23 Nov 2021 13:25:56 -0600
Subject: [PATCH 076/285] [maven-release-plugin] prepare release
clojure-1.11.0-alpha3
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 71c4378068..d9ae7e5f14 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.11.0-master-SNAPSHOT
+ 1.11.0-alpha3
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- HEAD
+ clojure-1.11.0-alpha3
From 7697d5da77a319077e71343f76fa204327881de4 Mon Sep 17 00:00:00 2001
From: "Hudson @ build.clojure.org"
Date: Tue, 23 Nov 2021 13:25:56 -0600
Subject: [PATCH 077/285] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index d9ae7e5f14..71c4378068 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.11.0-alpha3
+ 1.11.0-master-SNAPSHOT
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- clojure-1.11.0-alpha3
+ HEAD
From abe19832c0294fec4c9c55430c9262c4b6d2f8b1 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Tue, 30 Nov 2021 10:45:25 -0600
Subject: [PATCH 078/285] Remove/ignore project files
---
.gitignore | 1 +
...ven__org_clojure_data_generators_0_1_2.xml | 13 -----------
...ven__org_clojure_test_generative_0_4_0.xml | 13 -----------
clojure.iml | 23 -------------------
4 files changed, 1 insertion(+), 49 deletions(-)
delete mode 100644 .idea/libraries/Maven__org_clojure_data_generators_0_1_2.xml
delete mode 100644 .idea/libraries/Maven__org_clojure_test_generative_0_4_0.xml
delete mode 100644 clojure.iml
diff --git a/.gitignore b/.gitignore
index 5376efaf3f..18cf4cc05c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ nbproject/private/
maven-classpath
maven-classpath.properties
.idea/
+*.iml
diff --git a/.idea/libraries/Maven__org_clojure_data_generators_0_1_2.xml b/.idea/libraries/Maven__org_clojure_data_generators_0_1_2.xml
deleted file mode 100644
index 78f485eeb9..0000000000
--- a/.idea/libraries/Maven__org_clojure_data_generators_0_1_2.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_clojure_test_generative_0_4_0.xml b/.idea/libraries/Maven__org_clojure_test_generative_0_4_0.xml
deleted file mode 100644
index 78fac6dfd0..0000000000
--- a/.idea/libraries/Maven__org_clojure_test_generative_0_4_0.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/clojure.iml b/clojure.iml
deleted file mode 100644
index c1c8a6a84a..0000000000
--- a/clojure.iml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
From f5e5a4a7b8e0785436f0bbb9b91b6de9f754aa33 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 13 Jan 2022 11:57:31 -0600
Subject: [PATCH 079/285] CLJ-2684 Update contrib deps
Signed-off-by: Alex Miller
---
pom.xml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/pom.xml b/pom.xml
index 71c4378068..59159805a1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,7 +41,7 @@
org.clojure
spec.alpha
- 0.3.214
+ 0.3.218
org.clojure
@@ -51,7 +51,7 @@
org.clojure
test.generative
- 0.5.2
+ 1.0.0
test
@@ -63,7 +63,7 @@
org.clojure
test.check
- 0.9.0
+ 1.1.1
test
From b32e35ae89d291882dbdb662c4193ecfa9a593e8 Mon Sep 17 00:00:00 2001
From: Alexander Yakushev
Date: Thu, 13 Jan 2022 11:29:09 -0600
Subject: [PATCH 080/285] CLJ-2621: Make InstanceMethodExpr.emit discard unused
primitive returns without boxing
Signed-off-by: Alex Miller
---
src/jvm/clojure/lang/Compiler.java | 16 ++++++++++++----
test/clojure/test_clojure/java_interop.clj | 18 +++++++++++++++++-
2 files changed, 29 insertions(+), 5 deletions(-)
diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java
index 041786e888..ce16f85a21 100644
--- a/src/jvm/clojure/lang/Compiler.java
+++ b/src/jvm/clojure/lang/Compiler.java
@@ -1612,8 +1612,16 @@ public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
gen.invokeInterface(type, m);
else
gen.invokeVirtual(type, m);
- //if(context != C.STATEMENT || method.getReturnType() == Void.TYPE)
- HostExpr.emitBoxReturn(objx, gen, method.getReturnType());
+ Class retClass = method.getReturnType();
+ if(context == C.STATEMENT)
+ {
+ if(retClass == long.class || retClass == double.class)
+ gen.pop2();
+ else if(retClass != void.class)
+ gen.pop();
+ }
+ else
+ HostExpr.emitBoxReturn(objx, gen, retClass);
}
else
{
@@ -1627,9 +1635,9 @@ public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
method.emitClearLocals(gen);
}
gen.invokeStatic(REFLECTOR_TYPE, invokeInstanceMethodMethod);
+ if(context == C.STATEMENT)
+ gen.pop();
}
- if(context == C.STATEMENT)
- gen.pop();
}
public boolean hasJavaClass(){
diff --git a/test/clojure/test_clojure/java_interop.clj b/test/clojure/test_clojure/java_interop.clj
index 30fab56e56..cb21064b25 100644
--- a/test/clojure/test_clojure/java_interop.clj
+++ b/test/clojure/test_clojure/java_interop.clj
@@ -13,7 +13,8 @@
(:use clojure.test)
(:require [clojure.inspector]
[clojure.set :as set])
- (:import java.util.Base64))
+ (:import java.util.Base64
+ (java.util.concurrent.atomic AtomicLong AtomicInteger)))
; http://clojure.org/java_interop
; http://clojure.org/compilation
@@ -589,3 +590,18 @@
(is (= (char \a) \a)))
;; Note: More coercions in numbers.clj
+
+; Test that primitive boxing elision in statement context works
+; correctly (CLJ-2621)
+
+(defn inc-atomic-int [^AtomicInteger l]
+ (.incrementAndGet l)
+ nil)
+
+(defn inc-atomic-long [^AtomicLong l]
+ (.incrementAndGet l)
+ nil)
+
+(deftest test-boxing-prevention-when-compiling-statements
+ (is (= 1 (.get (doto (AtomicInteger. 0) inc-atomic-int))))
+ (is (= 1 (.get (doto (AtomicLong. 0) inc-atomic-long)))))
From b30a99014814f2fe68833622a567c80133b0c417 Mon Sep 17 00:00:00 2001
From: Fogus
Date: Thu, 13 Jan 2022 11:37:08 -0500
Subject: [PATCH 081/285] CLJ-2663: Added a guard in APVector#doEquiv so that
List operands that do not respond to #size are not checked. Also, added count
short-circuit logic to ASeq#equiv in an attempt to leverage fast-count
benefit, but the guard is more strict such that this and the operand should
bouth be Counted.
Signed-off-by: Alex Miller
---
src/jvm/clojure/lang/APersistentVector.java | 13 ++++++++-----
src/jvm/clojure/lang/ASeq.java | 5 +++++
test/clojure/test_clojure/vectors.clj | 8 ++++++++
3 files changed, 21 insertions(+), 5 deletions(-)
diff --git a/src/jvm/clojure/lang/APersistentVector.java b/src/jvm/clojure/lang/APersistentVector.java
index de8a09ea68..3e88d14dbf 100644
--- a/src/jvm/clojure/lang/APersistentVector.java
+++ b/src/jvm/clojure/lang/APersistentVector.java
@@ -98,15 +98,18 @@ static boolean doEquiv(IPersistentVector v, Object obj){
else if(obj instanceof List)
{
Collection ma = (Collection) obj;
- if(ma.size() != v.count())
+
+ if((!(ma instanceof IPersistentCollection) || (ma instanceof Counted)) && (ma.size() != v.count()))
return false;
- for(Iterator i1 = ((List) v).iterator(), i2 = ma.iterator();
- i1.hasNext();)
+
+ Iterator i2 = ma.iterator();
+
+ for(Iterator i1 = ((List) v).iterator(); i1.hasNext();)
{
- if(!Util.equiv(i1.next(), i2.next()))
+ if(!i2.hasNext() || !Util.equiv(i1.next(), i2.next()))
return false;
}
- return true;
+ return !i2.hasNext();
}
else
{
diff --git a/src/jvm/clojure/lang/ASeq.java b/src/jvm/clojure/lang/ASeq.java
index 325aa27c24..f0f8e5d447 100644
--- a/src/jvm/clojure/lang/ASeq.java
+++ b/src/jvm/clojure/lang/ASeq.java
@@ -37,6 +37,11 @@ public boolean equiv(Object obj){
if(!(obj instanceof Sequential || obj instanceof List))
return false;
+
+ if(this instanceof Counted && obj instanceof Counted &&
+ ((Counted)this).count() != ((Counted)obj).count())
+ return false;
+
ISeq ms = RT.seq(obj);
for(ISeq s = seq(); s != null; s = s.next(), ms = ms.next())
{
diff --git a/test/clojure/test_clojure/vectors.clj b/test/clojure/test_clojure/vectors.clj
index 0be0bf14e8..ea2b8e77d5 100644
--- a/test/clojure/test_clojure/vectors.clj
+++ b/test/clojure/test_clojure/vectors.clj
@@ -420,3 +420,11 @@
(deftest test-reduce-kv-vectors
(is (= 25 (reduce-kv + 10 [2 4 6])))
(is (= 25 (reduce-kv + 10 (subvec [0 2 4 6] 1)))))
+
+(deftest test-vector-eqv-to-non-counted-types
+ (is (not= (range) [0 1 2]))
+ (is (not= [0 1 2] (range)))
+ (is (= [0 1 2] (take 3 (range))))
+ (is (= [0 1 2] (new java.util.ArrayList [0 1 2])))
+ (is (not= [1 2] (take 1 (cycle [1 2]))))
+ (is (= [1 2 3 nil 4 5 6 nil] (eduction cat [[1 2 3 nil] [4 5 6 nil]]))))
From 304d7c6a81bd7d8511e9ef3d89dc199b1464afaa Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Wed, 12 Jan 2022 16:03:23 -0600
Subject: [PATCH 082/285] CLJ-2677 In clojure.math inline methods, use coercion
functions to resolve to single overload
Signed-off-by: Alex Miller
---
build.xml | 2 +-
codegen/gen_math.clj | 263 +++++++++++++++++++++++
src/clj/clojure/{java => }/math.clj | 141 +++++-------
test/clojure/test_clojure/math.clj | 320 ++++++++++++++++++++++++++++
4 files changed, 638 insertions(+), 88 deletions(-)
create mode 100644 codegen/gen_math.clj
rename src/clj/clojure/{java => }/math.clj (83%)
create mode 100644 test/clojure/test_clojure/math.clj
diff --git a/build.xml b/build.xml
index 444b5d9603..6796b3d986 100644
--- a/build.xml
+++ b/build.xml
@@ -76,7 +76,6 @@
-
@@ -86,6 +85,7 @@
+
diff --git a/codegen/gen_math.clj b/codegen/gen_math.clj
new file mode 100644
index 0000000000..484649d3d3
--- /dev/null
+++ b/codegen/gen_math.clj
@@ -0,0 +1,263 @@
+;; This code was used to generate the clojure.math namespace in
+;; Clojure 1.11 to wrap Java 1.8 java.lang.Math methods. There are
+;; many small tweaks in this to get exactly the output that was
+;; desired and it was not intended to be reused in any way, it is
+;; included here for future reference.
+
+(ns gen-math
+ (:require
+ [clojure.reflect :as reflect]
+ [clojure.set :as set]
+ [clojure.string :as str])
+ (:import
+ [java.io StringWriter Writer]))
+
+;; manually created
+(declare HEADER)
+(declare FNS)
+(declare DOCS)
+(declare ARGS)
+(declare ARGTYPES)
+
+(def const-template
+ "(def
+ ^{:doc %s
+ :added %s
+ :const true
+ :tag %s}
+ %s
+ %s)\n\n")
+
+(defn- emit-constant
+ [^Writer writer {:keys [cname name added type]}]
+ (let [sym (symbol (str cname) (str name))
+ doc (str "\"" (get DOCS (symbol name)) "\"")
+ tag (str "'" type)]
+ (.write writer
+ (format const-template doc (pr-str added) tag name sym))))
+
+(def fn-template
+ "(defn %s
+ {:doc %s
+ :inline-arities %s
+ :inline %s
+ :added %s}
+ %s%s
+ %s)\n\n")
+
+(defn- clojurize
+ [sym]
+ (or
+ (get '{IEEEremainder IEEE-remainder} sym)
+ (let [s (name sym)]
+ (symbol
+ (str
+ (reduce
+ (fn [^StringBuilder b ^Character c]
+ (if (Character/isUpperCase c)
+ (.. b (append "-") (append (Character/toLowerCase c)))
+ (.append b c)))
+ (StringBuilder.)
+ s))))))
+
+(defn- inline-body
+ [params param-types]
+ (str/join " "
+ (map (fn [p pt] (format "(%s ~%s)" pt p))
+ params param-types)))
+
+(defn- body
+ [params param-types on-types]
+ (map (fn [p pt] (if (contains? on-types pt) `(~pt ~p) p))
+ params param-types))
+
+(defn- emit-fn
+ [^Writer writer {:keys [cname fname sigs]}]
+ (let [sym (symbol (str cname) (str fname))
+ arities (group-by #(-> % :parameter-types count) sigs)
+ arity (-> arities keys first) ;; NOTE: ignore multiple arities, none in Math
+ arity-sigs (get arities arity)
+ cname (clojurize fname)
+ doc (str "\"" (get DOCS cname) "\"")
+ sig (if (= 1 (count arity-sigs)) (first arity-sigs) (get ARGTYPES cname))
+ {pts :parameter-types, rt :return-type} sig
+ ps (get ARGS cname)
+ ;; coerce all args in inline body
+ inline-body (format "(fn %s `(%s%s))" (pr-str ps) (if (< 0 (count ps)) (str sym " ") sym) (inline-body ps pts))
+ ;; ps are hinted, so coerce only ps that can't be hinted - int type
+ body `(~sym ~@(body ps pts #{'int}))
+ rts (if (#{'long 'double} rt) (str "^" rt " ") "")
+ hints (map #(if (#{'long 'double} %) (symbol (str "^" %)) nil) pts)
+ pst (vec (remove nil? (interleave hints ps)))]
+ (.write writer
+ (format fn-template cname doc #{arity} inline-body (pr-str "1.11") rts pst body))))
+
+(defn gen-static-wrappers
+ [csym]
+ (let [added "1.11"
+ members (:members (reflect/type-reflect (resolve csym)))
+ statics (filter #(set/subset? #{:public :static} (:flags %)) members)
+ {fs false, ms true} (group-by #(contains? % :return-type) statics)
+ methods (->> ms (filter (fn [m]
+ (or (= 'scalb (:name m))
+ (empty? (set/intersection #{'int 'float} (set (:parameter-types m))))))))
+ by-name (group-by :name methods)
+ writer (StringWriter.)]
+ (.write writer HEADER)
+ (doseq [f fs]
+ (emit-constant writer (merge f {:cname csym, :added added})))
+ (doseq [n FNS]
+ (emit-fn writer {:cname csym, :fname n, :added added, :sigs (get by-name n)}))
+ (spit "src/clj/clojure/math.clj" (str writer))))
+
+(comment
+ (gen-static-wrappers 'Math)
+ )
+
+;;;; Manually provided info used during the generator
+
+(def ^String HEADER
+ "; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html at the root of this distribution.
+; By using this software in any fashion, you are agreeing to be bound by
+; the terms of this license.
+; You must not remove this notice, or any other, from this software.
+
+(ns
+ ^{:author \"Alex Miller\",
+ :doc \"Clojure wrapper functions for java.lang.Math static methods.
+
+ Function calls are inlined for performance, and type hinted for primitive
+ long or double parameters where appropriate. In general, Math methods are
+ optimized for performance and have bounds for error tolerance. If
+ greater precision is needed, use java.lang.StrictMath directly instead.
+
+ For more complete information, see:
+ https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html\"}
+ clojure.math)
+
+(set! *warn-on-reflection* true)
+
+")
+
+;; fns
+
+;; omitted: toIntExact
+;; omitted but include in core w/polymorphic impl: abs, min, max
+(def FNS
+ '[sin cos tan asin acos atan toRadians toDegrees exp log log10
+ sqrt cbrt IEEEremainder ceil floor rint atan2 pow round random
+ addExact subtractExact multiplyExact incrementExact decrementExact negateExact
+ floorDiv floorMod ulp signum sinh cosh tanh hypot expm1 log1p copySign getExponent
+ nextAfter nextUp nextDown scalb])
+
+;; docstrings to use
+(def DOCS
+ '{
+ E "Constant for e, the base for natural logarithms.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#E"
+ PI "Constant for pi, the ratio of the circumference of a circle to its diameter.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#PI"
+ sin "Returns the sine of an angle.\n If a is ##NaN, ##-Inf, ##Inf => ##NaN\n If a is zero => zero with the same sign as a\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#sin-double-"
+ cos "Returns the cosine of an angle.\n If a is ##NaN, ##-Inf, ##Inf => ##NaN\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#cos-double-"
+ tan "Returns the tangent of an angle.\n If a is ##NaN, ##-Inf, ##Inf => ##NaN\n If a is zero => zero with the same sign as a\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#tan-double-"
+ asin "Returns the arc sine of an angle, in the range -pi/2 to pi/2.\n If a is ##NaN or |a|>1 => ##NaN\n If a is zero => zero with the same sign as a\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#asin-double-"
+ acos "Returns the arc cosine of a, in the range 0.0 to pi.\n If a is ##NaN or |a|>1 => ##NaN\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#acos-double-"
+ atan "Returns the arc tangent of a, in the range of -pi/2 to pi/2.\n If a is ##NaN => ##NaN\n If a is zero => zero with the same sign as a\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#atan-double-"
+ to-radians "Converts an angle in degrees to an approximate equivalent angle in radians.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#toRadians-double-"
+ to-degrees "Converts an angle in radians to an approximate equivalent angle in degrees.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#toDegrees-double-"
+ exp "Returns Euler's number e raised to the power of a.\n If a is ##NaN => ##NaN\n If a is ##Inf => ##Inf\n If a is ##-Inf => +0.0\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#exp-double-"
+ log "Returns the natural logarithm (base e) of a.\n If a is ##NaN or negative => ##NaN\n If a is ##Inf => ##Inf\n If a is zero => ##-Inf\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#log-double-"
+ log10 "Returns the logarithm (base 10) of a.\n If a is ##NaN or negative => ##NaN\n If a is ##Inf => ##Inf\n If a is zero => ##-Inf\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#log10-double-"
+ sqrt "Returns the positive square root of a.\n If a is ##NaN or negative => ##NaN\n If a is ##Inf => ##Inf\n If a is zero => a\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#sqrt-double-"
+ cbrt "Returns the cube root of a.\n If a is ##NaN => ##NaN\n If a is ##Inf or ##-Inf => a\n If a is zero => zero with sign matching a\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#cbrt-double-"
+ IEEE-remainder "Returns the remainder per IEEE 754 such that\n remainder = dividend - divisor * n\n where n is the integer closest to the exact value of dividend / divisor.\n If two integers are equally close, then n is the even one.\n If the remainder is zero, sign will match dividend.\n If dividend or divisor is ##NaN, or dividend is ##Inf or ##-Inf, or divisor is zero => ##NaN\n If dividend is finite and divisor is infinite => dividend\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#IEEEremainder-double-double-"
+ ceil "Returns the smallest double greater than or equal to a, and equal to a\n mathematical integer.\n If a is ##NaN or ##Inf or ##-Inf or already equal to an integer => a\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#ceil-double-"
+ floor "Returns the largest double less than or equal to a, and equal to a\n mathematical integer.\n If a is ##NaN or ##Inf or ##-Inf or already equal to an integer => a\n If a is less than zero but greater than -1.0 => -0.0\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#floor-double-"
+ rint "Returns the double closest to a and equal to a mathematical integer.\n If two values are equally close, return the even one.\n If a is ##NaN or ##Inf or ##-Inf or zero => a\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#rint-double-"
+ atan2 "Returns the angle theta from the conversion of rectangular coordinates (x, y) to polar coordinates (r, theta).\n Computes the phase theta by computing an arc tangent of y/x in the range of -pi to pi.\n For more details on special cases, see:\n https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#atan2-double-double-"
+ pow "Returns the value of a raised to the power of b.\n For more details on special cases, see:\n https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#pow-double-double-"
+ round "Returns the closest long to a. If equally close to two values, return the one\n closer to ##Inf.\n If a is ##NaN => 0\n If a is ##-Inf or < Long/MIN_VALUE => Long/MIN_VALUE\n If a is ##Inf or > Long/MAX_VALUE => Long/MAX_VALUE\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#round-double-"
+ random "Returns a positive double between 0.0 and 1.0, chosen pseudorandomly with\n approximately random distribution.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#random--"
+ add-exact "Returns the sum of x and y, throws ArithmeticException on overflow.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#addExact-long-long-"
+ subtract-exact "Returns the difference of x and y, throws ArithmeticException on overflow.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#subtractExact-long-long-"
+ multiply-exact "Returns the product of x and y, throws ArithmeticException on overflow.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#multiplyExact-long-long-"
+ increment-exact "Returns a incremented by 1, throws ArithmeticException on overflow.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#incrementExact-long-"
+ decrement-exact "Returns a decremented by 1, throws ArithmeticException on overflow.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#decrementExact-long-"
+ negate-exact "Returns the negation of a, throws ArithmeticException on overflow.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#negateExact-long-"
+ floor-div "Integer division that rounds to negative infinity (as opposed to zero).\n The special case (floorDiv Long/MIN_VALUE -1) overflows and returns Long/MIN_VALUE.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#floorDiv-long-long-"
+ floor-mod "Integer modulus x - (floorDiv(x, y) * y). Sign matches y and is in the\n range -|y| < r < |y|.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#floorMod-long-long-"
+ ulp "Returns the size of an ulp (unit in last place) for d.\n If d is ##NaN => ##NaN\n If d is ##Inf or ##-Inf => ##Inf\n If d is zero => Double/MIN_VALUE\n If d is +/- Double/MAX_VALUE => 2^971\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#ulp-double-"
+ signum "Returns the signum function of d - zero for zero, 1.0 if >0, -1.0 if <0.\n If d is ##NaN => ##NaN\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#signum-double-"
+ sinh "Returns the hyperbolic sine of x, (e^x - e^-x)/2.\n If x is ##NaN => ##NaN\n If x is ##Inf or ##-Inf or zero => x\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#sinh-double-"
+ cosh "Returns the hyperbolic cosine of x, (e^x + e^-x)/2.\n If x is ##NaN => ##NaN\n If x is ##Inf or ##-Inf => ##Inf\n If x is zero => 1.0\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#cosh-double-"
+ tanh "Returns the hyperbolic tangent of x, sinh(x)/cosh(x).\n If x is ##NaN => ##NaN\n If x is zero => zero, with same sign\n If x is ##Inf => +1.0\n If x is ##-Inf => -1.0\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#tanh-double-"
+ hypot "Returns sqrt(x^2 + y^2) without intermediate underflow or overflow.\n If x or y is ##Inf or ##-Inf => ##Inf\n If x or y is ##NaN and neither is ##Inf or ##-Inf => ##NaN\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#hypot-double-double-"
+ expm1 "Returns e^x - 1. Near 0, expm1(x)+1 is more accurate to e^x than exp(x).\n If x is ##NaN => ##NaN\n If x is ##Inf => #Inf\n If x is ##-Inf => -1.0\n If x is zero => x\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#expm1-double-"
+ log1p "Returns ln(1+x). For small values of x, log1p(x) is more accurate than\n log(1.0+x).\n If x is ##NaN or < -1 => ##NaN\n If x is ##Inf => ##Inf\n If x is -1 => ##-Inf\n If x is 0 => 0 with sign matching x\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#log1p-double-"
+ copy-sign "Returns a double with the magnitude of the first argument and the sign of\n the second.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#copySign-double-double-"
+ get-exponent "Returns the exponent of d.\n If d is ##NaN, ##Inf, ##-Inf => Double/MAX_EXPONENT + 1\n If d is zero or subnormal => Double/MIN_EXPONENT - 1\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#getExponent-double-"
+ next-after "Returns the adjacent floating point number to start in the direction of\n the second argument. If the arguments are equal, the second is returned.\n If either arg is #NaN => #NaN\n If both arguments are signed zeros => direction\n If start is +-Double/MIN_VALUE and direction would cause a smaller magnitude\n => zero with sign matching start\n If start is ##Inf or ##-Inf and direction would cause a smaller magnitude\n => Double/MAX_VALUE with same sign as start\n If start is equal to +=Double/MAX_VALUE and direction would cause a larger magnitude\n => ##Inf or ##-Inf with sign matching start\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextAfter-double-double-"
+ next-up "Returns the adjacent double of d in the direction of ##Inf.\n If d is ##NaN => ##NaN\n If d is ##Inf => ##Inf\n If d is zero => Double/MIN_VALUE\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextUp-double-"
+ next-down "Returns the adjacent double of d in the direction of ##-Inf.\n If d is ##NaN => ##NaN\n If d is ##-Inf => ##-Inf\n If d is zero => -Double/MIN_VALUE\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextDown-double-"
+ scalb "Returns d * 2^scaleFactor, scaling by a factor of 2. If the exponent\n is between Double/MIN_EXPONENT and Double/MAX_EXPONENT, the answer is exact.\n If d is ##NaN => ##NaN\n If d is ##Inf or ##-Inf => ##Inf or ##-Inf respectively\n If d is zero => zero of same sign as d\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextDown-double-"
+ })
+
+(def FNS
+ '[sin cos tan asin acos atan toRadians toDegrees exp log log10
+ sqrt cbrt IEEEremainder ceil floor rint atan2 pow round random
+ addExact subtractExact multiplyExact incrementExact decrementExact negateExact
+ floorDiv floorMod ulp signum sinh cosh tanh hypot expm1 log1p copySign getExponent
+ nextAfter nextUp nextDown scalb])
+
+;; arg names to use (match java.lang.Math signatures)
+(def ARGS
+ '{
+ sin [a]
+ cos [a]
+ tan [a]
+ asin [a]
+ acos [a]
+ atan [a]
+ to-radians [deg]
+ to-degrees [r]
+ exp [a]
+ log [a]
+ log10 [a]
+ sqrt [a]
+ cbrt [a]
+ IEEE-remainder [dividend divisor]
+ ceil [a]
+ floor [a]
+ rint [a]
+ atan2 [y x]
+ pow [a b]
+ round [a]
+ random []
+ add-exact [x y]
+ subtract-exact [x y]
+ multiply-exact [x y]
+ increment-exact [a]
+ decrement-exact [a]
+ negate-exact [a]
+ floor-div [x y]
+ floor-mod [x y]
+ ulp [d]
+ signum [d]
+ sinh [x]
+ cosh [x]
+ tanh [x]
+ hypot [x y]
+ expm1 [x]
+ log1p [x]
+ copy-sign [magnitude sign]
+ get-exponent [d]
+ next-after [start direction]
+ next-up [d]
+ next-down [d]
+ scalb [d scaleFactor]
+ })
+
+;; type signature to use (otherwise automatically determined)
+(def ARGTYPES
+ '{scalb {:parameter-types [double int] :return-type double}})
diff --git a/src/clj/clojure/java/math.clj b/src/clj/clojure/math.clj
similarity index 83%
rename from src/clj/clojure/java/math.clj
rename to src/clj/clojure/math.clj
index 2d54fb52d7..c5b67b1f1c 100644
--- a/src/clj/clojure/java/math.clj
+++ b/src/clj/clojure/math.clj
@@ -17,8 +17,9 @@
For more complete information, see:
https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html"}
- clojure.java.math
- (:refer-clojure :exclude [min max]))
+ clojure.math)
+
+(set! *warn-on-reflection* true)
(def
^{:doc "Constant for e, the base for natural logarithms.
@@ -44,7 +45,7 @@
If a is zero => zero with the same sign as a
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#sin-double-"
:inline-arities #{1}
- :inline (fn [a] `(Math/sin ~a))
+ :inline (fn [a] `(Math/sin (double ~a)))
:added "1.11"}
^double [^double a]
(Math/sin a))
@@ -54,7 +55,7 @@
If a is ##NaN, ##-Inf, ##Inf => ##NaN
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#cos-double-"
:inline-arities #{1}
- :inline (fn [a] `(Math/cos ~a))
+ :inline (fn [a] `(Math/cos (double ~a)))
:added "1.11"}
^double [^double a]
(Math/cos a))
@@ -65,7 +66,7 @@
If a is zero => zero with the same sign as a
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#tan-double-"
:inline-arities #{1}
- :inline (fn [a] `(Math/tan ~a))
+ :inline (fn [a] `(Math/tan (double ~a)))
:added "1.11"}
^double [^double a]
(Math/tan a))
@@ -76,7 +77,7 @@
If a is zero => zero with the same sign as a
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#asin-double-"
:inline-arities #{1}
- :inline (fn [a] `(Math/asin ~a))
+ :inline (fn [a] `(Math/asin (double ~a)))
:added "1.11"}
^double [^double a]
(Math/asin a))
@@ -86,7 +87,7 @@
If a is ##NaN or |a|>1 => ##NaN
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#acos-double-"
:inline-arities #{1}
- :inline (fn [a] `(Math/acos ~a))
+ :inline (fn [a] `(Math/acos (double ~a)))
:added "1.11"}
^double [^double a]
(Math/acos a))
@@ -97,7 +98,7 @@
If a is zero => zero with the same sign as a
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#atan-double-"
:inline-arities #{1}
- :inline (fn [a] `(Math/atan ~a))
+ :inline (fn [a] `(Math/atan (double ~a)))
:added "1.11"}
^double [^double a]
(Math/atan a))
@@ -106,7 +107,7 @@
{:doc "Converts an angle in degrees to an approximate equivalent angle in radians.
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#toRadians-double-"
:inline-arities #{1}
- :inline (fn [deg] `(Math/toRadians ~deg))
+ :inline (fn [deg] `(Math/toRadians (double ~deg)))
:added "1.11"}
^double [^double deg]
(Math/toRadians deg))
@@ -115,7 +116,7 @@
{:doc "Converts an angle in radians to an approximate equivalent angle in degrees.
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#toDegrees-double-"
:inline-arities #{1}
- :inline (fn [r] `(Math/toDegrees ~r))
+ :inline (fn [r] `(Math/toDegrees (double ~r)))
:added "1.11"}
^double [^double r]
(Math/toDegrees r))
@@ -127,7 +128,7 @@
If a is ##-Inf => +0.0
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#exp-double-"
:inline-arities #{1}
- :inline (fn [a] `(Math/exp ~a))
+ :inline (fn [a] `(Math/exp (double ~a)))
:added "1.11"}
^double [^double a]
(Math/exp a))
@@ -139,7 +140,7 @@
If a is zero => ##-Inf
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#log-double-"
:inline-arities #{1}
- :inline (fn [a] `(Math/log ~a))
+ :inline (fn [a] `(Math/log (double ~a)))
:added "1.11"}
^double [^double a]
(Math/log a))
@@ -151,7 +152,7 @@
If a is zero => ##-Inf
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#log10-double-"
:inline-arities #{1}
- :inline (fn [a] `(Math/log10 ~a))
+ :inline (fn [a] `(Math/log10 (double ~a)))
:added "1.11"}
^double [^double a]
(Math/log10 a))
@@ -163,7 +164,7 @@
If a is zero => a
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#sqrt-double-"
:inline-arities #{1}
- :inline (fn [a] `(Math/sqrt ~a))
+ :inline (fn [a] `(Math/sqrt (double ~a)))
:added "1.11"}
^double [^double a]
(Math/sqrt a))
@@ -175,7 +176,7 @@
If a is zero => zero with sign matching a
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#cbrt-double-"
:inline-arities #{1}
- :inline (fn [a] `(Math/cbrt ~a))
+ :inline (fn [a] `(Math/cbrt (double ~a)))
:added "1.11"}
^double [^double a]
(Math/cbrt a))
@@ -190,7 +191,7 @@
If dividend is finite and divisor is infinite => dividend
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#IEEEremainder-double-double-"
:inline-arities #{2}
- :inline (fn [dividend divisor] `(Math/IEEEremainder ~dividend ~divisor))
+ :inline (fn [dividend divisor] `(Math/IEEEremainder (double ~dividend) (double ~divisor)))
:added "1.11"}
^double [^double dividend ^double divisor]
(Math/IEEEremainder dividend divisor))
@@ -201,7 +202,7 @@
If a is ##NaN or ##Inf or ##-Inf or already equal to an integer => a
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#ceil-double-"
:inline-arities #{1}
- :inline (fn [a] `(Math/ceil ~a))
+ :inline (fn [a] `(Math/ceil (double ~a)))
:added "1.11"}
^double [^double a]
(Math/ceil a))
@@ -213,7 +214,7 @@
If a is less than zero but greater than -1.0 => -0.0
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#floor-double-"
:inline-arities #{1}
- :inline (fn [a] `(Math/floor ~a))
+ :inline (fn [a] `(Math/floor (double ~a)))
:added "1.11"}
^double [^double a]
(Math/floor a))
@@ -224,7 +225,7 @@
If a is ##NaN or ##Inf or ##-Inf or zero => a
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#rint-double-"
:inline-arities #{1}
- :inline (fn [a] `(Math/rint ~a))
+ :inline (fn [a] `(Math/rint (double ~a)))
:added "1.11"}
^double [^double a]
(Math/rint a))
@@ -235,7 +236,7 @@
For more details on special cases, see:
https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#atan2-double-double-"
:inline-arities #{2}
- :inline (fn [y x] `(Math/atan2 ~y ~x))
+ :inline (fn [y x] `(Math/atan2 (double ~y) (double ~x)))
:added "1.11"}
^double [^double y ^double x]
(Math/atan2 y x))
@@ -245,7 +246,7 @@
For more details on special cases, see:
https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#pow-double-double-"
:inline-arities #{2}
- :inline (fn [a b] `(Math/pow ~a ~b))
+ :inline (fn [a b] `(Math/pow (double ~a) (double ~b)))
:added "1.11"}
^double [^double a ^double b]
(Math/pow a b))
@@ -258,7 +259,7 @@
If a is ##Inf or > Long/MAX_VALUE => Long/MAX_VALUE
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#round-double-"
:inline-arities #{1}
- :inline (fn [a] `(Math/round ~a))
+ :inline (fn [a] `(Math/round (double ~a)))
:added "1.11"}
^long [^double a]
(Math/round a))
@@ -277,7 +278,7 @@
{:doc "Returns the sum of x and y, throws ArithmeticException on overflow.
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#addExact-long-long-"
:inline-arities #{2}
- :inline (fn [x y] `(Math/addExact ~x ~y))
+ :inline (fn [x y] `(Math/addExact (long ~x) (long ~y)))
:added "1.11"}
^long [^long x ^long y]
(Math/addExact x y))
@@ -286,7 +287,7 @@
{:doc "Returns the difference of x and y, throws ArithmeticException on overflow.
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#subtractExact-long-long-"
:inline-arities #{2}
- :inline (fn [x y] `(Math/subtractExact ~x ~y))
+ :inline (fn [x y] `(Math/subtractExact (long ~x) (long ~y)))
:added "1.11"}
^long [^long x ^long y]
(Math/subtractExact x y))
@@ -295,7 +296,7 @@
{:doc "Returns the product of x and y, throws ArithmeticException on overflow.
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#multiplyExact-long-long-"
:inline-arities #{2}
- :inline (fn [x y] `(Math/multiplyExact ~x ~y))
+ :inline (fn [x y] `(Math/multiplyExact (long ~x) (long ~y)))
:added "1.11"}
^long [^long x ^long y]
(Math/multiplyExact x y))
@@ -304,7 +305,7 @@
{:doc "Returns a incremented by 1, throws ArithmeticException on overflow.
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#incrementExact-long-"
:inline-arities #{1}
- :inline (fn [a] `(Math/incrementExact ~a))
+ :inline (fn [a] `(Math/incrementExact (long ~a)))
:added "1.11"}
^long [^long a]
(Math/incrementExact a))
@@ -313,16 +314,16 @@
{:doc "Returns a decremented by 1, throws ArithmeticException on overflow.
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#decrementExact-long-"
:inline-arities #{1}
- :inline (fn [a] `(Math/decrementExact ~a))
+ :inline (fn [a] `(Math/decrementExact (long ~a)))
:added "1.11"}
^long [^long a]
(Math/decrementExact a))
-(defn negative-exact
+(defn negate-exact
{:doc "Returns the negation of a, throws ArithmeticException on overflow.
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#negateExact-long-"
:inline-arities #{1}
- :inline (fn [a] `(Math/negateExact ~a))
+ :inline (fn [a] `(Math/negateExact (long ~a)))
:added "1.11"}
^long [^long a]
(Math/negateExact a))
@@ -332,7 +333,7 @@
The special case (floorDiv Long/MIN_VALUE -1) overflows and returns Long/MIN_VALUE.
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#floorDiv-long-long-"
:inline-arities #{2}
- :inline (fn [x y] `(Math/floorDiv ~x ~y))
+ :inline (fn [x y] `(Math/floorDiv (long ~x) (long ~y)))
:added "1.11"}
^long [^long x ^long y]
(Math/floorDiv x y))
@@ -342,47 +343,11 @@
range -|y| < r < |y|.
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#floorMod-long-long-"
:inline-arities #{2}
- :inline (fn [x y] `(Math/floorMod ~x ~y))
+ :inline (fn [x y] `(Math/floorMod (long ~x) (long ~y)))
:added "1.11"}
^long [^long x ^long y]
(Math/floorMod x y))
-(defn abs
- {:doc "Returns the absolute value of a (long or double).
- If not negative, a is returned, else negation of a is returned.
- If a is Long/MIN_VALUE => Long/MIN_VALUE
- If a is a double and zero => +0.0
- If a is a double and ##Inf or ##-Inf => ##Inf
- If a is a double and ##NaN => ##NaN
- See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#abs-long-"
- :inline-arities #{1}
- :inline (fn [a] `(Math/abs ~a))
- :added "1.11"}
- [a]
- (Math/abs a))
-
-(defn max
- {:doc "Returns the greater of a or b.
- If doubles and a or b is ##NaN => ##NaN
- If doubles and one is negative zero, other positive zero => positive zero
- See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#max-long-long-"
- :inline-arities #{2}
- :inline (fn [a b] `(Math/max ~a ~b))
- :added "1.11"}
- [a b]
- (Math/max a b))
-
-(defn min
- {:doc "Returns the lesser of a or b.
- If doubles and a or b is ##NaN => ##NaN
- If doubles and one is negative zero, other positive zero => negative zero
- See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#min-double-double-"
- :inline-arities #{2}
- :inline (fn [p0 p1] `(Math/min ~p0 ~p1))
- :added "1.11"}
- [p0 p1]
- (Math/min p0 p1))
-
(defn ulp
{:doc "Returns the size of an ulp (unit in last place) for d.
If d is ##NaN => ##NaN
@@ -391,7 +356,7 @@
If d is +/- Double/MAX_VALUE => 2^971
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#ulp-double-"
:inline-arities #{1}
- :inline (fn [d] `(Math/ulp ~d))
+ :inline (fn [d] `(Math/ulp (double ~d)))
:added "1.11"}
^double [^double d]
(Math/ulp d))
@@ -399,10 +364,9 @@
(defn signum
{:doc "Returns the signum function of d - zero for zero, 1.0 if >0, -1.0 if <0.
If d is ##NaN => ##NaN
- If d is ##Inf or ##-Inf => d
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#signum-double-"
:inline-arities #{1}
- :inline (fn [d] `(Math/signum ~d))
+ :inline (fn [d] `(Math/signum (double ~d)))
:added "1.11"}
^double [^double d]
(Math/signum d))
@@ -413,7 +377,7 @@
If x is ##Inf or ##-Inf or zero => x
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#sinh-double-"
:inline-arities #{1}
- :inline (fn [x] `(Math/sinh ~x))
+ :inline (fn [x] `(Math/sinh (double ~x)))
:added "1.11"}
^double [^double x]
(Math/sinh x))
@@ -425,7 +389,7 @@
If x is zero => 1.0
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#cosh-double-"
:inline-arities #{1}
- :inline (fn [x] `(Math/cosh ~x))
+ :inline (fn [x] `(Math/cosh (double ~x)))
:added "1.11"}
^double [^double x]
(Math/cosh x))
@@ -438,7 +402,7 @@
If x is ##-Inf => -1.0
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#tanh-double-"
:inline-arities #{1}
- :inline (fn [x] `(Math/tanh ~x))
+ :inline (fn [x] `(Math/tanh (double ~x)))
:added "1.11"}
^double [^double x]
(Math/tanh x))
@@ -449,7 +413,7 @@
If x or y is ##NaN and neither is ##Inf or ##-Inf => ##NaN
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#hypot-double-double-"
:inline-arities #{2}
- :inline (fn [x y] `(Math/hypot ~x ~y))
+ :inline (fn [x y] `(Math/hypot (double ~x) (double ~y)))
:added "1.11"}
^double [^double x ^double y]
(Math/hypot x y))
@@ -462,7 +426,7 @@
If x is zero => x
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#expm1-double-"
:inline-arities #{1}
- :inline (fn [x] `(Math/expm1 ~x))
+ :inline (fn [x] `(Math/expm1 (double ~x)))
:added "1.11"}
^double [^double x]
(Math/expm1 x))
@@ -471,10 +435,12 @@
{:doc "Returns ln(1+x). For small values of x, log1p(x) is more accurate than
log(1.0+x).
If x is ##NaN or < -1 => ##NaN
- If x is ##Inf or ##-Inf or x => x
+ If x is ##Inf => ##Inf
+ If x is -1 => ##-Inf
+ If x is 0 => 0 with sign matching x
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#log1p-double-"
:inline-arities #{1}
- :inline (fn [x] `(Math/log1p ~x))
+ :inline (fn [x] `(Math/log1p (double ~x)))
:added "1.11"}
^double [^double x]
(Math/log1p x))
@@ -484,7 +450,7 @@
the second.
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#copySign-double-double-"
:inline-arities #{2}
- :inline (fn [magnitude sign] `(Math/copySign ~magnitude ~sign))
+ :inline (fn [magnitude sign] `(Math/copySign (double ~magnitude) (double ~sign)))
:added "1.11"}
^double [^double magnitude ^double sign]
(Math/copySign magnitude sign))
@@ -495,7 +461,7 @@
If d is zero or subnormal => Double/MIN_EXPONENT - 1
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#getExponent-double-"
:inline-arities #{1}
- :inline (fn [d] `(Math/getExponent ~d))
+ :inline (fn [d] `(Math/getExponent (double ~d)))
:added "1.11"}
[^double d]
(Math/getExponent d))
@@ -513,7 +479,7 @@
=> ##Inf or ##-Inf with sign matching start
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextAfter-double-double-"
:inline-arities #{2}
- :inline (fn [start direction] `(Math/nextAfter ~start ~direction))
+ :inline (fn [start direction] `(Math/nextAfter (double ~start) (double ~direction)))
:added "1.11"}
^double [^double start ^double direction]
(Math/nextAfter start direction))
@@ -525,7 +491,7 @@
If d is zero => Double/MIN_VALUE
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextUp-double-"
:inline-arities #{1}
- :inline (fn [d] `(Math/nextUp ~d))
+ :inline (fn [d] `(Math/nextUp (double ~d)))
:added "1.11"}
^double [^double d]
(Math/nextUp d))
@@ -533,11 +499,11 @@
(defn next-down
{:doc "Returns the adjacent double of d in the direction of ##-Inf.
If d is ##NaN => ##NaN
- If d is ##Inf => ##-Inf
- If d is zero => Double/MIN_VALUE
+ If d is ##-Inf => ##-Inf
+ If d is zero => -Double/MIN_VALUE
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextDown-double-"
:inline-arities #{1}
- :inline (fn [d] `(Math/nextDown ~d))
+ :inline (fn [d] `(Math/nextDown (double ~d)))
:added "1.11"}
^double [^double d]
(Math/nextDown d))
@@ -549,8 +515,9 @@
If d is ##Inf or ##-Inf => ##Inf or ##-Inf respectively
If d is zero => zero of same sign as d
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextDown-double-"
- :inline-arities #{1}
- :inline (fn [d scaleFactor] `(Math/scalb ~d ~scaleFactor))
+ :inline-arities #{2}
+ :inline (fn [d scaleFactor] `(Math/scalb (double ~d) (int ~scaleFactor)))
:added "1.11"}
^double [^double d scaleFactor]
- (Math/scalb d scaleFactor))
\ No newline at end of file
+ (Math/scalb d (int scaleFactor)))
+
diff --git a/test/clojure/test_clojure/math.clj b/test/clojure/test_clojure/math.clj
new file mode 100644
index 0000000000..63975e7505
--- /dev/null
+++ b/test/clojure/test_clojure/math.clj
@@ -0,0 +1,320 @@
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html at the root of this distribution.
+; By using this software in any fashion, you are agreeing to be bound by
+; the terms of this license.
+; You must not remove this notice, or any other, from this software.
+
+(ns clojure.test-clojure.math
+ (:require
+ [clojure.test :refer :all]
+ [clojure.math :as m]))
+
+(set! *warn-on-reflection* true)
+
+(defn neg-zero?
+ [^double d]
+ (and (zero? d) (< (Double/compare d 0.0) 0)))
+
+(defn pos-zero?
+ [^double d]
+ (and (zero? d) (not (< (Double/compare d 0.0) 0))))
+
+(deftest test-sin
+ (is (NaN? (m/sin ##NaN)))
+ (is (NaN? (m/sin ##-Inf)))
+ (is (NaN? (m/sin ##Inf)))
+ (is (pos-zero? (m/sin 0.0)))
+ (is (neg-zero? (m/sin -0.0)))
+ (is (= (m/sin m/PI) (- (m/sin (- m/PI))))))
+
+(deftest test-cos
+ (is (NaN? (m/cos ##NaN)))
+ (is (NaN? (m/cos ##-Inf)))
+ (is (NaN? (m/cos ##Inf)))
+ (is (= 1.0 (m/cos 0.0) (m/cos -0.0)))
+ (is (= (m/cos m/PI) (m/cos (- m/PI)))))
+
+(deftest test-tan
+ (is (NaN? (m/tan ##NaN)))
+ (is (NaN? (m/tan ##-Inf)))
+ (is (NaN? (m/tan ##Inf)))
+ (is (pos-zero? (m/tan 0.0)))
+ (is (neg-zero? (m/tan -0.0)))
+ (is (= (- (m/tan m/PI)) (m/tan (- m/PI)))))
+
+(deftest test-asin
+ (is (NaN? (m/asin ##NaN)))
+ (is (NaN? (m/asin 2.0)))
+ (is (NaN? (m/asin -2.0)))
+ (is (zero? (m/asin -0.0))))
+
+(deftest test-acos
+ (is (NaN? (m/acos ##NaN)))
+ (is (NaN? (m/acos -2.0)))
+ (is (NaN? (m/acos 2.0)))
+ (is (= m/PI (* 2 (m/acos 0.0)))))
+
+(deftest test-atan
+ (is (NaN? (m/atan ##NaN)))
+ (is (pos-zero? (m/atan 0.0)))
+ (is (neg-zero? (m/atan -0.0)))
+ (is (= 0.7853981633974483 (m/atan 1))))
+
+(deftest test-radians-degrees-roundtrip
+ (doseq [d (range 0.0 360.0 5.0)]
+ (is (= (m/round d) (m/round (-> d m/to-radians m/to-degrees))))))
+
+(deftest test-exp
+ (is (NaN? (m/exp ##NaN)))
+ (is (= ##Inf (m/exp ##Inf)))
+ (is (pos-zero? (m/exp ##-Inf)))
+ (is (= 1.0 (m/exp 0.0)))
+ (is (= m/E (m/exp 1))))
+
+(deftest test-log
+ (is (NaN? (m/log ##NaN)))
+ (is (NaN? (m/log -1.0)))
+ (is (= ##Inf (m/log ##Inf)))
+ (is (= ##-Inf (m/log 0.0)))
+ (is (= 1.0 (m/log m/E))))
+
+(deftest test-log10
+ (is (NaN? (m/log10 ##NaN)))
+ (is (NaN? (m/log10 -1.0)))
+ (is (= ##Inf (m/log10 ##Inf)))
+ (is (= ##-Inf (m/log10 0.0)))
+ (is (= 1.0 (m/log10 10))))
+
+(deftest test-sqrt
+ (is (NaN? (m/sqrt ##NaN)))
+ (is (NaN? (m/sqrt -1.0)))
+ (is (= ##Inf (m/sqrt ##Inf)))
+ (is (pos-zero? (m/sqrt 0)))
+ (is (= 2.0 (m/sqrt 4.0))))
+
+(deftest test-cbrt
+ (is (NaN? (m/cbrt ##NaN)))
+ (is (= ##-Inf (m/cbrt ##-Inf)))
+ (is (= ##Inf (m/cbrt ##Inf)))
+ (is (pos-zero? (m/cbrt 0)))
+ (is (= 2.0 (m/cbrt 8.0))))
+
+(deftest test-IEEE-remainder
+ (is (NaN? (m/IEEE-remainder ##NaN 1.0)))
+ (is (NaN? (m/IEEE-remainder 1.0 ##NaN)))
+ (is (NaN? (m/IEEE-remainder ##Inf 2.0)))
+ (is (NaN? (m/IEEE-remainder ##-Inf 2.0)))
+ (is (NaN? (m/IEEE-remainder 2 0.0)))
+ (is (= 1.0 (m/IEEE-remainder 5.0 4.0))))
+
+(deftest test-ceil
+ (is (NaN? (m/ceil ##NaN)))
+ (is (= ##Inf (m/ceil ##Inf)))
+ (is (= ##-Inf (m/ceil ##-Inf)))
+ (is (= 4.0 (m/ceil m/PI))))
+
+(deftest test-floor
+ (is (NaN? (m/floor ##NaN)))
+ (is (= ##Inf (m/floor ##Inf)))
+ (is (= ##-Inf (m/floor ##-Inf)))
+ (is (= 3.0 (m/floor m/PI))))
+
+(deftest test-rint
+ (is (NaN? (m/rint ##NaN)))
+ (is (= ##Inf (m/rint ##Inf)))
+ (is (= ##-Inf (m/rint ##-Inf)))
+ (is (= 1.0 (m/rint 1.2)))
+ (is (neg-zero? (m/rint -0.01))))
+
+(deftest test-atan2
+ (is (NaN? (m/atan2 ##NaN 1.0)))
+ (is (NaN? (m/atan2 1.0 ##NaN)))
+ (is (pos-zero? (m/atan2 0.0 1.0)))
+ (is (neg-zero? (m/atan2 -0.0 1.0)))
+ (is (= m/PI (m/atan2 0.0 -1.0)))
+ (is (= (- m/PI) (m/atan2 -0.0 -1.0)))
+ (is (= m/PI (* 2.0 (m/atan2 1.0 0.0))))
+ (is (= m/PI (* -2.0 (m/atan2 -1.0 0.0))))
+ (is (= m/PI (* 4.0 (m/atan2 ##Inf ##Inf))))
+ (is (= m/PI (/ (* 4.0 (m/atan2 ##Inf ##-Inf)) 3.0)))
+ (is (= m/PI (* -4.0 (m/atan2 ##-Inf ##Inf))))
+ (is (= m/PI (/ (* -4.0 (m/atan2 ##-Inf ##-Inf)) 3.0))))
+
+(deftest test-pow
+ (is (= 1.0 (m/pow 4.0 0.0)))
+ (is (= 1.0 (m/pow 4.0 -0.0)))
+ (is (= 4.2 (m/pow 4.2 1.0)))
+ (is (NaN? (m/pow 4.2 ##NaN)))
+ (is (NaN? (m/pow ##NaN 2.0)))
+ (is (= ##Inf (m/pow 2.0 ##Inf)))
+ (is (= ##Inf (m/pow 0.5 ##-Inf)))
+ (is (= 0.0 (m/pow 2.0 ##-Inf)))
+ (is (= 0.0 (m/pow 0.5 ##Inf)))
+ (is (NaN? (m/pow 1.0 ##Inf)))
+ (is (pos-zero? (m/pow 0.0 1.5)))
+ (is (pos-zero? (m/pow ##Inf -2.0)))
+ (is (= ##Inf (m/pow 0.0 -2.0)))
+ (is (= ##Inf (m/pow ##Inf 2.0)))
+ (is (pos-zero? (m/pow -0.0 1.5)))
+ (is (pos-zero? (m/pow ##-Inf -1.5)))
+ (is (neg-zero? (m/pow -0.0 3.0)))
+ (is (neg-zero? (m/pow ##-Inf -3.0)))
+ (is (= ##Inf (m/pow -0.0 -1.5)))
+ (is (= ##Inf (m/pow ##-Inf 2.5)))
+ (is (= ##-Inf (m/pow -0.0 -3.0)))
+ (is (= ##-Inf (m/pow ##-Inf 3.0)))
+ (is (= 4.0 (m/pow -2.0 2.0)))
+ (is (= -8.0 (m/pow -2.0 3.0)))
+ (is (= 8.0 (m/pow 2.0 3.0))))
+
+(deftest test-round
+ (is (= 0 (m/round ##NaN)))
+ (is (= Long/MIN_VALUE (m/round ##-Inf)))
+ (is (= Long/MIN_VALUE (m/round (- Long/MIN_VALUE 2.0))))
+ (is (= Long/MAX_VALUE (m/round ##Inf)))
+ (is (= Long/MAX_VALUE (m/round (+ Long/MAX_VALUE 2.0))))
+ (is (= 4 (m/round 3.5))))
+
+(deftest test-add-exact
+ (try
+ (m/add-exact Long/MAX_VALUE 1)
+ (is false)
+ (catch ArithmeticException _
+ (is true))))
+
+(deftest test-subtract-exact
+ (try
+ (m/subtract-exact Long/MIN_VALUE 1)
+ (is false)
+ (catch ArithmeticException _
+ (is true))))
+
+(deftest test-multiply-exact
+ (try
+ (m/multiply-exact Long/MAX_VALUE 2)
+ (is false)
+ (catch ArithmeticException _
+ (is true))))
+
+(deftest test-increment-exact
+ (try
+ (m/increment-exact Long/MAX_VALUE)
+ (is false)
+ (catch ArithmeticException _
+ (is true))))
+
+(deftest test-decrement-exact
+ (try
+ (m/decrement-exact Long/MIN_VALUE)
+ (is false)
+ (catch ArithmeticException _
+ (is true))))
+
+(deftest test-negate-exact
+ (is (= (inc Long/MIN_VALUE) (m/negate-exact Long/MAX_VALUE)))
+ (try
+ (m/negate-exact Long/MIN_VALUE)
+ (is false)
+ (catch ArithmeticException _
+ (is true))))
+
+(deftest test-floor-div
+ (is (= Long/MIN_VALUE (m/floor-div Long/MIN_VALUE -1)))
+ (is (= -1 (m/floor-div -2 5))))
+
+(deftest test-floor-mod
+ (is (= 3 (m/floor-mod -2 5))))
+
+(deftest test-ulp
+ (is (NaN? (m/ulp ##NaN)))
+ (is (= ##Inf (m/ulp ##Inf)))
+ (is (= ##Inf (m/ulp ##-Inf)))
+ (is (= Double/MIN_VALUE (m/ulp 0.0)))
+ (is (= (m/pow 2 971) (m/ulp Double/MAX_VALUE)))
+ (is (= (m/pow 2 971) (m/ulp (- Double/MAX_VALUE)))))
+
+(deftest test-signum
+ (is (NaN? (m/signum ##NaN)))
+ (is (zero? (m/signum 0.0)))
+ (is (zero? (m/signum -0.0)))
+ (is (= 1.0 (m/signum 42.0)))
+ (is (= -1.0 (m/signum -42.0))))
+
+(deftest test-sinh
+ (is (NaN? (m/sinh ##NaN)))
+ (is (= ##Inf (m/sinh ##Inf)))
+ (is (= ##-Inf (m/sinh ##-Inf)))
+ (is (= 0.0 (m/sinh 0.0))))
+
+(deftest test-cosh
+ (is (NaN? (m/cosh ##NaN)))
+ (is (= ##Inf (m/cosh ##Inf)))
+ (is (= ##Inf (m/cosh ##-Inf)))
+ (is (= 1.0 (m/cosh 0.0))))
+
+(deftest test-tanh
+ (is (NaN? (m/tanh ##NaN)))
+ (is (= 1.0 (m/tanh ##Inf)))
+ (is (= -1.0 (m/tanh ##-Inf)))
+ (is (= 0.0 (m/tanh 0.0))))
+
+(deftest test-hypot
+ (is (= ##Inf (m/hypot 1.0 ##Inf)))
+ (is (= ##Inf (m/hypot ##Inf 1.0)))
+ (is (NaN? (m/hypot ##NaN 1.0)))
+ (is (NaN? (m/hypot 1.0 ##NaN)))
+ (is (= 13.0 (m/hypot 5.0 12.0))))
+
+(deftest test-expm1
+ (is (NaN? (m/expm1 ##NaN)))
+ (is (= ##Inf (m/expm1 ##Inf)))
+ (is (= -1.0 (m/expm1 ##-Inf)))
+ (is (= 0.0 (m/expm1 0.0))))
+
+(deftest test-log1p
+ (is (NaN? (m/log1p ##NaN)))
+ (is (= ##Inf (m/log1p ##Inf)))
+ (is (= ##-Inf (m/log1p -1.0)))
+ (is (pos-zero? (m/log1p 0.0)))
+ (is (neg-zero? (m/log1p -0.0))))
+
+(deftest test-copy-sign
+ (is (= 1.0 (m/copy-sign 1.0 42.0)))
+ (is (= -1.0 (m/copy-sign 1.0 -42.0)))
+ (is (= -1.0 (m/copy-sign 1.0 ##-Inf))))
+
+(deftest test-get-exponent
+ (is (= (inc Double/MAX_EXPONENT) (m/get-exponent ##NaN)))
+ (is (= (inc Double/MAX_EXPONENT) (m/get-exponent ##Inf)))
+ (is (= (inc Double/MAX_EXPONENT) (m/get-exponent ##-Inf)))
+ (is (= (dec Double/MIN_EXPONENT) (m/get-exponent 0.0)))
+ (is (= 0 (m/get-exponent 1.0)))
+ (is (= 13 (m/get-exponent 12345.678))))
+
+(deftest test-next-after
+ (is (NaN? (m/next-after ##NaN 1)))
+ (is (NaN? (m/next-after 1 ##NaN)))
+ (is (pos-zero? (m/next-after 0.0 0.0)))
+ (is (neg-zero? (m/next-after -0.0 -0.0)))
+ (is (= Double/MAX_VALUE (m/next-after ##Inf 1.0)))
+ (is (pos-zero? (m/next-after Double/MIN_VALUE -1.0))))
+
+(deftest test-next-up
+ (is (NaN? (m/next-up ##NaN)))
+ (is (= ##Inf (m/next-up ##Inf)))
+ (is (= Double/MIN_VALUE (m/next-up 0.0))))
+
+(deftest test-next-down
+ (is (NaN? (m/next-down ##NaN)))
+ (is (= ##-Inf (m/next-down ##-Inf)))
+ (is (= (- Double/MIN_VALUE) (m/next-down 0.0))))
+
+(deftest test-scalb
+ (is (NaN? (m/scalb ##NaN 1)))
+ (is (= ##Inf (m/scalb ##Inf 1)))
+ (is (= ##-Inf (m/scalb ##-Inf 1)))
+ (is (pos-zero? (m/scalb 0.0 2)))
+ (is (neg-zero? (m/scalb -0.0 2)))
+ (is (= 32.0 (m/scalb 2.0 4))))
From ff61321179a0b0f7d2c17a394ade80465ac15c28 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Mon, 10 Jan 2022 13:52:31 -0600
Subject: [PATCH 083/285] CLJ-2673 Implement abs/min/max in clojure.core with
polymorphic numeric support
Signed-off-by: Alex Miller
---
src/clj/clojure/core.clj | 12 +++++++
src/jvm/clojure/lang/Numbers.java | 51 +++++++++++++++++++++------
test/clojure/test_clojure/numbers.clj | 14 ++++++++
3 files changed, 67 insertions(+), 10 deletions(-)
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 0dba6fcd97..7c8cb1804a 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -1134,6 +1134,18 @@
([x y & more]
(reduce1 min (min x y) more)))
+(defn abs
+ {:doc "Returns the absolute value of a.
+ If a is Long/MIN_VALUE => Long/MIN_VALUE
+ If a is a double and zero => +0.0
+ If a is a double and ##Inf or ##-Inf => ##Inf
+ If a is a double and ##NaN => ##NaN"
+ :inline-arities #{1}
+ :inline (fn [a] `(clojure.lang.Numbers/abs ~a))
+ :added "1.11"}
+ [a]
+ (clojure.lang.Numbers/abs a))
+
(defn dec'
"Returns a number one less than num. Supports arbitrary precision.
See also: dec"
diff --git a/src/jvm/clojure/lang/Numbers.java b/src/jvm/clojure/lang/Numbers.java
index fa13cba62c..6405bc2193 100644
--- a/src/jvm/clojure/lang/Numbers.java
+++ b/src/jvm/clojure/lang/Numbers.java
@@ -68,6 +68,8 @@ static interface Ops{
public Number dec(Number x);
public Number decP(Number x);
public Number unchecked_dec(Number x);
+
+ public Number abs(Number x);
}
static abstract class OpsP implements Ops{
@@ -619,6 +621,10 @@ public Number unchecked_dec(Number x){
long val = x.longValue();
return num(Numbers.unchecked_dec(val));
}
+
+ public Number abs(Number x){
+ return num(Math.abs(x.longValue()));
+ }
}
final static class DoubleOps extends OpsP{
@@ -706,6 +712,10 @@ public Number inc(Number x){
public Number dec(Number x){
return Double.valueOf(x.doubleValue() - 1);
}
+
+ public Number abs(Number x) {
+ return num(Math.abs(x.doubleValue()));
+ }
}
final static class RatioOps extends OpsP{
@@ -837,6 +847,11 @@ public Number dec(Number x){
return Numbers.add(x, -1);
}
+ public Number abs(Number x) {
+ Ratio r = (Ratio) x;
+ return new Ratio(r.numerator.abs(), r.denominator);
+ }
+
}
final static class BigIntOps extends OpsP{
@@ -935,6 +950,10 @@ public Number dec(Number x){
BigInteger bx = toBigInteger(x);
return BigInt.fromBigInteger(bx.subtract(BigInteger.ONE));
}
+
+ public Number abs(Number x) {
+ return BigInt.fromBigInteger(toBigInteger(x).abs());
+ }
}
@@ -1054,6 +1073,14 @@ public Number dec(Number x){
? bx.subtract(BigDecimal.ONE)
: bx.subtract(BigDecimal.ONE, mc);
}
+
+ public Number abs(Number x) {
+ MathContext mc = (MathContext) MATH_CONTEXT.deref();
+ BigDecimal bx = (BigDecimal) x;
+ return mc == null
+ ? ((BigDecimal) x).abs()
+ : ((BigDecimal) x).abs(mc);
+ }
}
static final LongOps LONG_OPS = new LongOps();
@@ -4068,11 +4095,7 @@ static public Object max(long x, double y){
static public long max(long x, long y){
- if(x > y) {
- return x;
- } else {
- return y;
- }
+ return Math.max(x, y);
}
@@ -4166,11 +4189,7 @@ static public Object min(long x, double y){
static public long min(long x, long y){
- if(x < y) {
- return x;
- } else {
- return y;
- }
+ return Math.min(x, y);
}
static public Object min(long x, Object y){
@@ -4221,4 +4240,16 @@ static public Object min(Object x, Object y){
}
}
+static public long abs(long x){
+ return Math.abs(x);
+}
+
+static public double abs(double x){
+ return Math.abs(x);
+}
+
+static public Number abs(Object x) {
+ return ops(x).abs((Number)x);
+}
+
}
diff --git a/test/clojure/test_clojure/numbers.clj b/test/clojure/test_clojure/numbers.clj
index 26140a7c29..cc62a63c89 100644
--- a/test/clojure/test_clojure/numbers.clj
+++ b/test/clojure/test_clojure/numbers.clj
@@ -644,6 +644,20 @@ Math/pow overflows to Infinity."
(is (= java.lang.Long (class (min 1.0 2.0 -10))))
(is (= java.lang.Double (class (min 1 2 -10.0 3 4 5))))))
+(deftest test-abs
+ (are [in ex] (= ex (abs in))
+ -1 1
+ 1 1
+ Long/MIN_VALUE Long/MIN_VALUE ;; special case!
+ -1.0 1.0
+ -0.0 0.0
+ ##-Inf ##Inf
+ ##Inf ##Inf
+ -123.456M 123.456M
+ -123N 123N
+ -1/5 1/5)
+ (is (NaN? (abs ##NaN))))
+
(deftest clj-868
(testing "min/max: NaN is contagious"
(letfn [(fnan? [^Float x] (Float/isNaN x))
From bf0c0e23a0e05509c785598be19b01c2b8e80bb5 Mon Sep 17 00:00:00 2001
From: Paula Gearon
Date: Tue, 14 Dec 2021 10:44:30 -0500
Subject: [PATCH 084/285] CLJ-2679 quoting condition values that are generated
for case-hash collisions
Signed-off-by: Alex Miller
---
src/clj/clojure/core.clj | 2 +-
test/clojure/test_clojure/control.clj | 9 ++++++++-
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 7c8cb1804a..72a4548eb3 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -6702,7 +6702,7 @@ fails, attempts to require sym's namespace and retries."
(next ks) (next vs))
m))
assoc-multi (fn [m h bucket]
- (let [testexprs (apply concat bucket)
+ (let [testexprs (mapcat (fn [kv] [(list 'quote (first kv)) (second kv)]) bucket)
expr `(condp = ~expr-sym ~@testexprs ~default)]
(assoc m h expr)))
hmap (reduce1
diff --git a/test/clojure/test_clojure/control.clj b/test/clojure/test_clojure/control.clj
index 92846ad38d..f3fe436b45 100644
--- a/test/clojure/test_clojure/control.clj
+++ b/test/clojure/test_clojure/control.clj
@@ -421,7 +421,14 @@
:b 1
:c -2
:d 4294967296
- :d 3))
+ :d 3)
+ (are [result input] (= result (case input
+ #{a} :set
+ :foo :keyword
+ a :symbol))
+ :symbol 'a
+ :keyword :foo
+ :set '#{a}))
(testing "test warn for hash collision"
(should-print-err-message
#"Performance warning, .*:\d+ - hash collision of some case test constants; if selected, those entries will be tested sequentially..*\r?\n"
From 0cbf655d181c2de8cd30bf234e40488fc94dbdfa Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Tue, 30 Nov 2021 08:19:41 -0600
Subject: [PATCH 085/285] CLJ-2670 - Use Math exact methods where possible for
perf
Signed-off-by: Alex Miller
---
src/jvm/clojure/lang/Numbers.java | 29 ++++++---------------------
src/jvm/clojure/lang/RT.java | 5 +----
test/clojure/test_clojure/numbers.clj | 2 +-
3 files changed, 8 insertions(+), 28 deletions(-)
diff --git a/src/jvm/clojure/lang/Numbers.java b/src/jvm/clojure/lang/Numbers.java
index 6405bc2193..82d8e6914c 100644
--- a/src/jvm/clojure/lang/Numbers.java
+++ b/src/jvm/clojure/lang/Numbers.java
@@ -1915,10 +1915,7 @@ static public Number unchecked_dec(Object x){
static public double remainder(long x, double y){return remainder((double)x,y);}
static public long add(long x, long y){
- long ret = x + y;
- if ((ret ^ x) < 0 && (ret ^ y) < 0)
- return throwIntOverflow();
- return ret;
+ return Math.addExact(x, y);
}
static public Number addP(long x, long y){
@@ -1929,10 +1926,7 @@ static public Number addP(long x, long y){
}
static public long minus(long x, long y){
- long ret = x - y;
- if (((ret ^ x) < 0 && (ret ^ ~y) < 0))
- return throwIntOverflow();
- return ret;
+ return Math.subtractExact(x, y);
}
static public Number minusP(long x, long y){
@@ -1943,9 +1937,7 @@ static public Number minusP(long x, long y){
}
static public long minus(long x){
- if(x == Long.MIN_VALUE)
- return throwIntOverflow();
- return -x;
+ return Math.negateExact(x);
}
static public Number minusP(long x){
@@ -1955,9 +1947,7 @@ static public Number minusP(long x){
}
static public long inc(long x){
- if(x == Long.MAX_VALUE)
- return throwIntOverflow();
- return x + 1;
+ return Math.incrementExact(x);
}
static public Number incP(long x){
@@ -1967,9 +1957,7 @@ static public Number incP(long x){
}
static public long dec(long x){
- if(x == Long.MIN_VALUE)
- return throwIntOverflow();
- return x - 1;
+ return Math.decrementExact(x);
}
static public Number decP(long x){
@@ -1980,12 +1968,7 @@ static public Number decP(long x){
static public long multiply(long x, long y){
- if (x == Long.MIN_VALUE && y < 0)
- return throwIntOverflow();
- long ret = x * y;
- if (y != 0 && ret/y != x)
- return throwIntOverflow();
- return ret;
+ return Math.multiplyExact(x, y);
}
static public Number multiplyP(long x, long y){
diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java
index 74399cf15f..5d20ef4964 100644
--- a/src/jvm/clojure/lang/RT.java
+++ b/src/jvm/clojure/lang/RT.java
@@ -1244,10 +1244,7 @@ static public int intCast(float x){
}
static public int intCast(long x){
- int i = (int) x;
- if(i != x)
- throw new IllegalArgumentException("Value out of range for int: " + x);
- return i;
+ return Math.toIntExact(x);
}
static public int intCast(double x){
diff --git a/test/clojure/test_clojure/numbers.clj b/test/clojure/test_clojure/numbers.clj
index cc62a63c89..a6b61605ea 100644
--- a/test/clojure/test_clojure/numbers.clj
+++ b/test/clojure/test_clojure/numbers.clj
@@ -181,7 +181,7 @@
(let [wrapped (fn [x]
(try
(f x)
- (catch IllegalArgumentException e :error)))]
+ (catch RuntimeException e :error)))]
(is (= vals (map wrapped inputs)))))))
;; *** Functions ***
From 4b9eadccce2aaf97e64bcc8e35c05c529df8fdd2 Mon Sep 17 00:00:00 2001
From: Hugo Duncan
Date: Wed, 12 Mar 2014 14:38:38 -0400
Subject: [PATCH 086/285] Fix quoting in assert-predicate
Signed-off-by: Alex Miller
---
src/clj/clojure/test.clj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/clj/clojure/test.clj b/src/clj/clojure/test.clj
index 0ab20a75a3..fc3536d7f3 100644
--- a/src/clj/clojure/test.clj
+++ b/src/clj/clojure/test.clj
@@ -447,7 +447,7 @@
result# (apply ~pred values#)]
(if result#
(do-report {:type :pass, :message ~msg,
- :expected '~form, :actual (cons ~pred values#)})
+ :expected '~form, :actual (cons '~pred values#)})
(do-report {:type :fail, :message ~msg,
:expected '~form, :actual (list '~'not (cons '~pred values#))}))
result#)))
From f08a81ba946cb7afaea859822bba1f52004326ec Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Mon, 20 Sep 2021 15:06:45 -0500
Subject: [PATCH 087/285] CLJ-2234 - Use explicit multimethod hierarchy in
preference checks
Signed-off-by: Alex Miller
---
src/jvm/clojure/lang/MultiFn.java | 26 +++++++--------
test/clojure/test_clojure/multimethods.clj | 37 ++++++++++++++++++++++
2 files changed, 50 insertions(+), 13 deletions(-)
diff --git a/src/jvm/clojure/lang/MultiFn.java b/src/jvm/clojure/lang/MultiFn.java
index fd37e2a085..44ac1ce1c0 100644
--- a/src/jvm/clojure/lang/MultiFn.java
+++ b/src/jvm/clojure/lang/MultiFn.java
@@ -85,7 +85,7 @@ public MultiFn preferMethod(Object dispatchValX, Object dispatchValY) {
rw.writeLock().lock();
try
{
- if(prefers(dispatchValY, dispatchValX))
+ if(prefers(hierarchy.deref(), dispatchValY, dispatchValX))
throw new IllegalStateException(
String.format("Preference conflict in multimethod '%s': %s is already preferred to %s",
name, dispatchValY, dispatchValX));
@@ -102,29 +102,29 @@ public MultiFn preferMethod(Object dispatchValX, Object dispatchValY) {
}
}
-private boolean prefers(Object x, Object y) {
+private boolean prefers(Object hierarchy, Object x, Object y) {
IPersistentSet xprefs = (IPersistentSet) getPreferTable().valAt(x);
if(xprefs != null && xprefs.contains(y))
return true;
- for(ISeq ps = RT.seq(parents.invoke(y)); ps != null; ps = ps.next())
+ for(ISeq ps = RT.seq(parents.invoke(hierarchy, y)); ps != null; ps = ps.next())
{
- if(prefers(x, ps.first()))
+ if(prefers(hierarchy, x, ps.first()))
return true;
}
- for(ISeq ps = RT.seq(parents.invoke(x)); ps != null; ps = ps.next())
+ for(ISeq ps = RT.seq(parents.invoke(hierarchy, x)); ps != null; ps = ps.next())
{
- if(prefers(ps.first(), y))
+ if(prefers(hierarchy, ps.first(), y))
return true;
}
return false;
}
-private boolean isA(Object x, Object y) {
- return RT.booleanCast(isa.invoke(hierarchy.deref(), x, y));
+private boolean isA(Object hierarchy, Object x, Object y) {
+ return RT.booleanCast(isa.invoke(hierarchy, x, y));
}
-private boolean dominates(Object x, Object y) {
- return prefers(x, y) || isA(x, y);
+private boolean dominates(Object hierarchy, Object x, Object y) {
+ return prefers(hierarchy, x, y) || isA(hierarchy, x, y);
}
private IPersistentMap resetCache() {
@@ -170,11 +170,11 @@ private IFn findAndCacheBestMethod(Object dispatchVal) {
for(Object o : getMethodTable())
{
Map.Entry e = (Map.Entry) o;
- if(isA(dispatchVal, e.getKey()))
+ if(isA(ch, dispatchVal, e.getKey()))
{
- if(bestEntry == null || dominates(e.getKey(), bestEntry.getKey()))
+ if(bestEntry == null || dominates(ch, e.getKey(), bestEntry.getKey()))
bestEntry = e;
- if(!dominates(bestEntry.getKey(), e.getKey()))
+ if(!dominates(ch, bestEntry.getKey(), e.getKey()))
throw new IllegalArgumentException(
String.format(
"Multiple methods in multimethod '%s' match dispatch value: %s -> %s and %s, and neither is preferred",
diff --git a/test/clojure/test_clojure/multimethods.clj b/test/clojure/test_clojure/multimethods.clj
index 924b0bcbeb..435f0f64a5 100644
--- a/test/clojure/test_clojure/multimethods.clj
+++ b/test/clojure/test_clojure/multimethods.clj
@@ -203,6 +203,43 @@
(testing "The prefers method now returns the correct table"
(is (= {[::rect ::shape] #{[::shape ::rect]}} (prefers bar)))))
+(deftest indirect-preferences-mulitmethod-test
+ (testing "Using global hierarchy"
+ (derive ::parent-1 ::grandparent-1)
+ (derive ::parent-2 ::grandparent-2)
+ (derive ::child ::parent-1)
+ (derive ::child ::parent-2)
+ (testing "x should be preferred over y if x is preferred over an ancestor of y"
+ (defmulti indirect-1 keyword)
+ (prefer-method indirect-1 ::parent-1 ::grandparent-2)
+ (defmethod indirect-1 ::parent-1 [_] ::parent-1)
+ (defmethod indirect-1 ::parent-2 [_] ::parent-2)
+ (is (= ::parent-1 (indirect-1 ::child))))
+ (testing "x should be preferred over y if an ancestor of x is preferred over y"
+ (defmulti indirect-2 keyword)
+ (prefer-method indirect-2 ::grandparent-1 ::parent-2)
+ (defmethod indirect-2 ::parent-1 [_] ::parent-1)
+ (defmethod indirect-2 ::parent-2 [_] ::parent-2)
+ (is (= ::parent-1 (indirect-2 ::child)))))
+ (testing "Using custom hierarchy"
+ (def local-h (-> (make-hierarchy)
+ (derive :parent-1 :grandparent-1)
+ (derive :parent-2 :grandparent-2)
+ (derive :child :parent-1)
+ (derive :child :parent-2)))
+ (testing "x should be preferred over y if x is preferred over an ancestor of y"
+ (defmulti indirect-3 keyword :hierarchy #'local-h)
+ (prefer-method indirect-3 :parent-1 :grandparent-2)
+ (defmethod indirect-3 :parent-1 [_] :parent-1)
+ (defmethod indirect-3 :parent-2 [_] :parent-2)
+ (is (= :parent-1 (indirect-3 :child))))
+ (testing "x should be preferred over y if an ancestor of x is preferred over y"
+ (defmulti indirect-4 keyword :hierarchy #'local-h)
+ (prefer-method indirect-4 :grandparent-1 :parent-2)
+ (defmethod indirect-4 :parent-1 [_] :parent-1)
+ (defmethod indirect-4 :parent-2 [_] :parent-2)
+ (is (= :parent-1 (indirect-4 :child))))))
+
(deftest remove-all-methods-test
(testing "Core function remove-all-methods works"
(defmulti simple1 identity)
From c33a5abca7828843b7018a8aa28e63dd40310f86 Mon Sep 17 00:00:00 2001
From: Jonathan D Johnston
Date: Thu, 21 Mar 2019 20:59:30 -0400
Subject: [PATCH 088/285] CLJ-2493: Prevent browse-url from hanging when using
xdg-open (updated)
Signed-off-by: Alex Miller
---
src/clj/clojure/java/browse.clj | 2 +-
src/clj/clojure/java/shell.clj | 20 ++++++++++++++++++++
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/src/clj/clojure/java/browse.clj b/src/clj/clojure/java/browse.clj
index 6fcc650756..c416e53832 100644
--- a/src/clj/clojure/java/browse.clj
+++ b/src/clj/clojure/java/browse.clj
@@ -71,6 +71,6 @@
script (if (= :uninitialized script)
(reset! *open-url-script* (open-url-script-val))
script)]
- (or (when script (sh/sh script (str url)) true)
+ (or (when script (sh/launch script (str url)) true)
(open-url-in-browser url)
(open-url-in-swing url))))
diff --git a/src/clj/clojure/java/shell.clj b/src/clj/clojure/java/shell.clj
index cb3f90a276..8f167086df 100644
--- a/src/clj/clojure/java/shell.clj
+++ b/src/clj/clojure/java/shell.clj
@@ -127,6 +127,21 @@ collecting its stdout"}
exit-code (.waitFor proc)]
{:exit exit-code :out @out :err @err}))))
+(defn launch
+ "Same as sh except the I/O channels of the sub-process are ignored.
+
+ Use when you want the sub-process to take an action, but do not want to wait
+ for the I/O channels to close. Example: Launching a browser with xdg-open.
+ "
+ {:added "1.11"}
+ [& args]
+ (let [[cmd opts] (parse-args args)
+ proc (.exec (Runtime/getRuntime)
+ ^"[Ljava.lang.String;" (into-array cmd)
+ (as-env-strings (:env opts))
+ (as-file (:dir opts)))]
+ {:exit (.waitFor proc)}))
+
(comment
(println (sh "ls" "-l"))
@@ -139,4 +154,9 @@ collecting its stdout"}
(println (sh "cat" "myimage.png" :out-enc :bytes)) ; reads binary file into bytes[]
(println (sh "cmd" "/c dir 1>&2"))
+(println (launch "touch" "my-test-file"))
+(println (sh "ls" "-l" "my-test-file"))
+(println (launch "rm" "my-test-file"))
+(println (sh "ls" "-l" "my-test-file"))
+
)
From b962791cbb201e91c29003f2ae1f7cd7a908971f Mon Sep 17 00:00:00 2001
From: Ghadi Shayban
Date: Fri, 16 Oct 2020 16:13:10 -0400
Subject: [PATCH 089/285] CLJ-2556 complete into's process consistently
Signed-off-by: Alex Miller
---
src/clj/clojure/core.clj | 6 +++++-
test/clojure/test_clojure/transducers.clj | 10 ++++++++++
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 72a4548eb3..30500de3e9 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -6961,7 +6961,11 @@ fails, attempts to require sym's namespace and retries."
(reduce conj to from)))
([to xform from]
(if (instance? clojure.lang.IEditableCollection to)
- (with-meta (persistent! (transduce xform conj! (transient to) from)) (meta to))
+ (let [tm (meta to)
+ rf (fn
+ ([coll] (-> (persistent! coll) (with-meta tm)))
+ ([coll v] (conj! coll v)))]
+ (transduce xform rf (transient to) from))
(transduce xform conj to from))))
(defn mapv
diff --git a/test/clojure/test_clojure/transducers.clj b/test/clojure/test_clojure/transducers.clj
index 76a0e978a3..b7a9c665e2 100644
--- a/test/clojure/test_clojure/transducers.clj
+++ b/test/clojure/test_clojure/transducers.clj
@@ -398,3 +398,13 @@
(sequence (map-indexed vector) [])))
(is (= [[0 1] [1 2] [2 3] [3 4]]
(sequence (map-indexed vector) (range 1 5)))))
+
+(deftest test-into+halt-when
+ (is (= :anomaly (into [] (comp (filter some?) (halt-when #{:anomaly}))
+ [1 2 3 :anomaly 4])))
+ (is (= {:anomaly :oh-no!,
+ :partial-results [1 2]}
+ (into []
+ (halt-when :anomaly #(assoc %2 :partial-results %1))
+ [1 2 {:anomaly :oh-no!} 3 4]))))
+
From c01f10bd8c68bd16c657fe352b206573f9306168 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 21 Oct 2021 22:40:07 -0500
Subject: [PATCH 090/285] CLJ-2665 Modify change for :as-alias to load if :as
or :use also included
Signed-off-by: Alex Miller
---
src/clj/clojure/core.clj | 31 +++++++++++++--------------
test/clojure/test_clojure/ns_libs.clj | 12 ++++++-----
2 files changed, 22 insertions(+), 21 deletions(-)
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 30500de3e9..055050b530 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -5960,26 +5960,25 @@
opts (apply hash-map options)
{:keys [as reload reload-all require use verbose as-alias]} opts
loaded (contains? @*loaded-libs* lib)
- load (cond reload-all
- load-all
- (or reload (not require) (not loaded))
- load-one)
need-ns (or as use)
+ load (cond reload-all load-all
+ reload load-one
+ (not loaded) (cond need-ns load-one
+ as-alias (fn [lib _need _require] (create-ns lib))
+ :else load-one))
+
filter-opts (select-keys opts '(:exclude :only :rename :refer))
undefined-on-entry (not (find-ns lib))]
(binding [*loading-verbosely* (or *loading-verbosely* verbose)]
- (if as-alias
- (when (not (find-ns lib))
- (create-ns lib))
- (if load
- (try
- (load lib need-ns require)
- (catch Exception e
- (when undefined-on-entry
- (remove-ns lib))
- (throw e)))
- (throw-if (and need-ns (not (find-ns lib)))
- "namespace '%s' not found" lib)))
+ (if load
+ (try
+ (load lib need-ns require)
+ (catch Exception e
+ (when undefined-on-entry
+ (remove-ns lib))
+ (throw e)))
+ (throw-if (and need-ns (not (find-ns lib)))
+ "namespace '%s' not found" lib))
(when (and need-ns *loading-verbosely*)
(printf "(clojure.core/in-ns '%s)\n" (ns-name *ns*)))
(when as
diff --git a/test/clojure/test_clojure/ns_libs.clj b/test/clojure/test_clojure/ns_libs.clj
index a9f4f97f71..256f99ae87 100644
--- a/test/clojure/test_clojure/ns_libs.clj
+++ b/test/clojure/test_clojure/ns_libs.clj
@@ -116,16 +116,18 @@
(is (= :not.a.real.ns.foo/baz (read-string "::foo/baz")))
- ;; can use :as-alias in use
- (use '[dummy.example1 :as-alias e1])
- (let [aliases (ns-aliases *ns*)]
- (is (= 'dummy.example1 (ns-name (get aliases 'e1)))))
+ ;; can use :as-alias in use, but load will occur
+ (use '[clojure.walk :as-alias e1])
+ (is (= 'clojure.walk (ns-name (get (ns-aliases *ns*) 'e1))))
+ (is (= :clojure.walk/walk (read-string "::e1/walk")))
;; can use both :as and :as-alias
(require '[clojure.set :as n1 :as-alias n2])
(let [aliases (ns-aliases *ns*)]
(is (= 'clojure.set (ns-name (get aliases 'n1))))
- (is (= 'clojure.set (ns-name (get aliases 'n2))))))
+ (is (= 'clojure.set (ns-name (get aliases 'n2))))
+ (is (= (resolve 'n1/union) #'clojure.set/union))
+ (is (= (resolve 'n2/union) #'clojure.set/union))))
(deftest require-as-alias-then-load-later
;; alias but don't load
From 4a4a6e7717d411679820c4a3ce735a77aef45cc3 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 2 Dec 2021 13:37:05 -0600
Subject: [PATCH 091/285] CLJ-2611 Disallow XXE by default in clojure.xml/parse
Signed-off-by: Alex Miller
---
src/clj/clojure/xml.clj | 40 ++++++++++++++++++++---
test/clojure/test_clojure/clojure_xml.clj | 13 ++++++--
2 files changed, 47 insertions(+), 6 deletions(-)
diff --git a/src/clj/clojure/xml.clj b/src/clj/clojure/xml.clj
index 4e4220f265..d892e19279 100644
--- a/src/clj/clojure/xml.clj
+++ b/src/clj/clojure/xml.clj
@@ -72,8 +72,36 @@
(skippedEntity [name])
))))
-(defn startparse-sax [s ch]
- (.. SAXParserFactory (newInstance) (newSAXParser) (parse s ch)))
+(defn sax-parser
+ "Create a new SAXParser"
+ {:added "1.11"}
+ ^SAXParser []
+ (.newSAXParser (SAXParserFactory/newInstance)))
+
+(defn disable-external-entities
+ "Modifies a SAXParser to disable external entity resolution to prevent XXE attacks"
+ {:added "1.11"}
+ ^SAXParser [^SAXParser parser]
+ (let [reader (.getXMLReader parser)]
+ ;; as per https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
+ (.setFeature reader "http://apache.org/xml/features/nonvalidating/load-external-dtd" false)
+ (.setFeature reader "http://xml.org/sax/features/external-general-entities", false)
+ (.setFeature reader "http://xml.org/sax/features/external-parameter-entities" false)
+ parser))
+
+(defn startparse-sax
+ "A startparse function suitable for use with clojure.xml/parse.
+ Note that this function is open to XXE entity attacks, see startparse-sax-safe."
+ {:added "1.0"}
+ [s ch]
+ (.parse (sax-parser) s ch))
+
+(defn startparse-sax-safe
+ "A startparse function suitable for use with clojure.xml/parse.
+ External entity resolution is disabled to prevent XXE entity attacks."
+ {:added "1.11"}
+ [s ch]
+ (.parse (disable-external-entities (sax-parser)) s ch))
(defn parse
"Parses and loads the source s, which can be a File, InputStream or
@@ -81,9 +109,13 @@
which has the keys :tag, :attrs, and :content. and accessor fns tag,
attrs, and content. Other parsers can be supplied by passing
startparse, a fn taking a source and a ContentHandler and returning
- a parser"
+ a parser.
+
+ Prior to 1.11, used startparse-sax by default. As of 1.11, uses
+ startparse-sax-safe, which disables XXE (XML External Entity)
+ processing. Pass startparse-sax to revert to prior behavior."
{:added "1.0"}
- ([s] (parse s startparse-sax))
+ ([s] (parse s startparse-sax-safe))
([s startparse]
(binding [*stack* nil
*current* (struct element)
diff --git a/test/clojure/test_clojure/clojure_xml.clj b/test/clojure/test_clojure/clojure_xml.clj
index cf7eb9508d..d37962f361 100644
--- a/test/clojure/test_clojure/clojure_xml.clj
+++ b/test/clojure/test_clojure/clojure_xml.clj
@@ -11,9 +11,18 @@
(ns clojure.test-clojure.clojure-xml
(:use clojure.test)
- (:require [clojure.xml :as xml]))
-
+ (:require [clojure.xml :as xml])
+ (:import [java.io ByteArrayInputStream]))
+(deftest CLJ-2611-avoid-XXE
+ (let [xml-str "
+
+ ]>
+&xxe; "]
+ (is (= {:tag :foo, :attrs nil, :content nil}
+ (with-open [input (ByteArrayInputStream. (.getBytes xml-str))]
+ (xml/parse input))))))
; parse
; emit-element
From e45e47882597359aa2adce9f244ecdba730e6c76 Mon Sep 17 00:00:00 2001
From: Ghadi Shayban
Date: Thu, 13 Jan 2022 14:27:57 -0600
Subject: [PATCH 092/285] CLJ-2555: clojure.core/iteration
Subsumes CLJ-1906
Signed-off-by: Alex Miller
---
src/clj/clojure/core.clj | 43 +++++++++++++
test/clojure/test_clojure/sequences.clj | 85 +++++++++++++++++++++++++
2 files changed, 128 insertions(+)
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 055050b530..4db32604a6 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -7783,6 +7783,49 @@ fails, attempts to require sym's namespace and retries."
(reduce #(proc %2) nil coll)
nil)
+(defn iteration
+ "creates a seqable/reducible given step!,
+ a function of some (opaque continuation data) k
+
+ step! - fn of k/nil to (opaque) 'ret'
+
+ :some? - fn of ret -> truthy, indicating there is a value
+ will not call vf/kf nor continue when false
+ :vf - fn of ret -> v, the values produced by the iteration
+ :kf - fn of ret -> next-k or nil (will not continue)
+ :initk - the first value passed to step!
+
+ vf, kf default to identity, some? defaults to some?, initk defaults to nil
+
+ it is presumed that step! with non-initk is unreproducible/non-idempotent
+ if step! with initk is unreproducible, it is on the consumer to not consume twice"
+ {:added "1.11"}
+ [step! & {:keys [vf kf some? initk]
+ :or {vf identity
+ kf identity
+ some? some?
+ initk nil}}]
+ (reify
+ clojure.lang.Seqable
+ (seq [_]
+ ((fn next [ret]
+ (when (some? ret)
+ (cons (vf ret)
+ (when-some [k (kf ret)]
+ (lazy-seq (next (step! k)))))))
+ (step! initk)))
+ clojure.lang.IReduceInit
+ (reduce [_ rf init]
+ (loop [acc init
+ ret (step! initk)]
+ (if (some? ret)
+ (let [acc (rf acc (vf ret))]
+ (if (reduced? acc)
+ @acc
+ (if-some [k (kf ret)]
+ (recur acc (step! k))
+ acc)))
+ acc)))))
(defn tagged-literal?
"Return true if the value is the data representation of a tagged literal"
diff --git a/test/clojure/test_clojure/sequences.clj b/test/clojure/test_clojure/sequences.clj
index 54d640f72d..96e6775bdd 100644
--- a/test/clojure/test_clojure/sequences.clj
+++ b/test/clojure/test_clojure/sequences.clj
@@ -1384,3 +1384,88 @@
(when (reversible? coll)
(is (= true (instance? clojure.lang.IMeta (rseq coll))))
(is (= {:a true} (meta (with-meta (rseq coll) {:a true})))))))
+
+(deftest test-iteration-opts
+ (let [genstep (fn [steps]
+ (fn [k] (swap! steps inc) (inc k)))
+ test (fn [expect & iteropts]
+ (is (= expect
+ (let [nsteps (atom 0)
+ iter (apply iteration (genstep nsteps) iteropts)
+ ret (doall (seq iter))]
+ {:ret ret :steps @nsteps})
+ (let [nsteps (atom 0)
+ iter (apply iteration (genstep nsteps) iteropts)
+ ret (into [] iter)]
+ {:ret ret :steps @nsteps}))))]
+ (test {:ret [1 2 3 4]
+ :steps 5}
+ :initk 0 :some? #(< % 5))
+ (test {:ret [1 2 3 4 5]
+ :steps 5}
+ :initk 0 :kf (fn [ret] (when (< ret 5) ret)))
+ (test {:ret ["1"]
+ :steps 2}
+ :initk 0 :some? #(< % 2) :vf str))
+
+ ;; kf does not stop on false
+ (let [iter #(iteration (fn [k]
+ (if (boolean? k)
+ [10 :boolean]
+ [k k]))
+ :vf second
+ :kf (fn [[k v]]
+ (cond
+ (= k 3) false
+ (< k 14) (inc k)))
+ :initk 0)]
+ (is (= [0 1 2 3 :boolean 11 12 13 14]
+ (into [] (iter))
+ (seq (iter))))))
+
+(deftest test-iteration
+ ;; equivalence to line-seq
+ (let [readme #(java.nio.file.Files/newBufferedReader (.toPath (java.io.File. "readme.txt")))]
+ (is (= (with-open [r (readme)]
+ (vec (iteration (fn [_] (.readLine r)))))
+ (with-open [r (readme)]
+ (doall (line-seq r))))))
+
+ ;; paginated API
+ (let [items 12 pgsize 5
+ src (vec (repeatedly items #(java.util.UUID/randomUUID)))
+ api (fn [tok]
+ (let [tok (or tok 0)]
+ (when (< tok items)
+ {:tok (+ tok pgsize)
+ :ret (subvec src tok (min (+ tok pgsize) items))})))]
+ (is (= src
+ (mapcat identity (iteration api :kf :tok :vf :ret))
+ (into [] cat (iteration api :kf :tok :vf :ret)))))
+
+ (let [src [:a :b :c :d :e]
+ api (fn [k]
+ (let [k (or k 0)]
+ (if (< k (count src))
+ {:item (nth src k)
+ :k (inc k)})))]
+ (is (= [:a :b :c]
+ (vec (iteration api
+ :some? (comp #{:a :b :c} :item)
+ :kf :k
+ :vf :item))
+ (vec (iteration api
+ :kf #(some-> % :k #{0 1 2})
+ :vf :item))))))
+
+(defspec iteration-seq-equals-reduce 100
+ (prop/for-all [initk gen/int
+ seed gen/int]
+ (let [src (fn []
+ (let [rng (java.util.Random. seed)]
+ (iteration #(unchecked-add % (.nextLong rng))
+ :some? (complement #(zero? (mod % 1000)))
+ :vf str
+ :initk initk)))]
+ (= (into [] (src))
+ (seq (src))))))
\ No newline at end of file
From 5451cee06b9e31513a19e596e4e155d1f08d2a8d Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 13 Jan 2022 14:38:37 -0600
Subject: [PATCH 093/285] CLJ-2680 Primitive local bindings can have matching
type hint
Signed-off-by: Alex Miller
---
src/jvm/clojure/lang/Compiler.java | 9 +++++++--
test/clojure/test_clojure/numbers.clj | 3 +++
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java
index ce16f85a21..cacaeb0f0e 100644
--- a/src/jvm/clojure/lang/Compiler.java
+++ b/src/jvm/clojure/lang/Compiler.java
@@ -6024,9 +6024,14 @@ public static class LocalBindingExpr implements Expr, MaybePrimitiveExpr, Assign
public LocalBindingExpr(LocalBinding b, Symbol tag)
{
if(b.getPrimitiveType() != null && tag != null)
- throw new UnsupportedOperationException("Can't type hint a primitive local");
+ if(! b.getPrimitiveType().equals(tagClass(tag)))
+ throw new UnsupportedOperationException("Can't type hint a primitive local with a different type");
+ else
+ this.tag = null;
+ else
+ this.tag = tag;
+
this.b = b;
- this.tag = tag;
this.clearPath = (PathNode)CLEAR_PATH.get();
this.clearRoot = (PathNode)CLEAR_ROOT.get();
diff --git a/test/clojure/test_clojure/numbers.clj b/test/clojure/test_clojure/numbers.clj
index a6b61605ea..bf822cc947 100644
--- a/test/clojure/test_clojure/numbers.clj
+++ b/test/clojure/test_clojure/numbers.clj
@@ -184,6 +184,9 @@
(catch RuntimeException e :error)))]
(is (= vals (map wrapped inputs)))))))
+(deftest test-prim-with-matching-hint
+ (is (= 1 (let [x 1.2] (Math/round ^double x)))))
+
;; *** Functions ***
(defonce DELTA 1e-12)
From 2b3ba822815981e7f76907a3b75e9ecc428f7656 Mon Sep 17 00:00:00 2001
From: Ghadi Shayban
Date: Thu, 20 Jan 2022 10:54:35 -0600
Subject: [PATCH 094/285] CLJ-2690 Clarify iteration docstring, rename args
Signed-off-by: Alex Miller
---
src/clj/clojure/core.clj | 41 +++++++++++++------------
test/clojure/test_clojure/sequences.clj | 8 ++---
2 files changed, 26 insertions(+), 23 deletions(-)
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 4db32604a6..42abb7f00a 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -7784,46 +7784,49 @@ fails, attempts to require sym's namespace and retries."
nil)
(defn iteration
- "creates a seqable/reducible given step!,
- a function of some (opaque continuation data) k
+ "Creates a seqable/reducible via repeated calls to step,
+ a function of some (continuation token) 'k'. The first call to step
+ will be passed initk, returning 'ret'. Iff (somef ret) is true,
+ (vf ret) will be included in the iteration, else iteration will
+ terminate and vf/kf will not be called. If (kf ret) is non-nil it
+ will be passed to the next step call, else iteration will terminate.
- step! - fn of k/nil to (opaque) 'ret'
+ This can be used e.g. to consume APIs that return paginated or batched data.
- :some? - fn of ret -> truthy, indicating there is a value
- will not call vf/kf nor continue when false
- :vf - fn of ret -> v, the values produced by the iteration
- :kf - fn of ret -> next-k or nil (will not continue)
- :initk - the first value passed to step!
+ step - (possibly impure) fn of 'k' -> 'ret'
- vf, kf default to identity, some? defaults to some?, initk defaults to nil
+ :somef - fn of 'ret' -> logical true/false, default 'some?'
+ :vf - fn of 'ret' -> 'v', a value produced by the iteration, default 'identity'
+ :kf - fn of 'ret' -> 'next-k' or nil (signaling 'do not continue'), default 'identity'
+ :initk - the first value passed to step, default 'nil'
- it is presumed that step! with non-initk is unreproducible/non-idempotent
- if step! with initk is unreproducible, it is on the consumer to not consume twice"
+ It is presumed that step with non-initk is unreproducible/non-idempotent.
+ If step with initk is unreproducible it is on the consumer to not consume twice."
{:added "1.11"}
- [step! & {:keys [vf kf some? initk]
+ [step & {:keys [somef vf kf initk]
:or {vf identity
kf identity
- some? some?
+ somef some?
initk nil}}]
(reify
clojure.lang.Seqable
(seq [_]
((fn next [ret]
- (when (some? ret)
+ (when (somef ret)
(cons (vf ret)
(when-some [k (kf ret)]
- (lazy-seq (next (step! k)))))))
- (step! initk)))
+ (lazy-seq (next (step k)))))))
+ (step initk)))
clojure.lang.IReduceInit
(reduce [_ rf init]
(loop [acc init
- ret (step! initk)]
- (if (some? ret)
+ ret (step initk)]
+ (if (somef ret)
(let [acc (rf acc (vf ret))]
(if (reduced? acc)
@acc
(if-some [k (kf ret)]
- (recur acc (step! k))
+ (recur acc (step k))
acc)))
acc)))))
diff --git a/test/clojure/test_clojure/sequences.clj b/test/clojure/test_clojure/sequences.clj
index 96e6775bdd..1fc93cd8ce 100644
--- a/test/clojure/test_clojure/sequences.clj
+++ b/test/clojure/test_clojure/sequences.clj
@@ -1400,13 +1400,13 @@
{:ret ret :steps @nsteps}))))]
(test {:ret [1 2 3 4]
:steps 5}
- :initk 0 :some? #(< % 5))
+ :initk 0 :somef #(< % 5))
(test {:ret [1 2 3 4 5]
:steps 5}
:initk 0 :kf (fn [ret] (when (< ret 5) ret)))
(test {:ret ["1"]
:steps 2}
- :initk 0 :some? #(< % 2) :vf str))
+ :initk 0 :somef #(< % 2) :vf str))
;; kf does not stop on false
(let [iter #(iteration (fn [k]
@@ -1451,7 +1451,7 @@
:k (inc k)})))]
(is (= [:a :b :c]
(vec (iteration api
- :some? (comp #{:a :b :c} :item)
+ :somef (comp #{:a :b :c} :item)
:kf :k
:vf :item))
(vec (iteration api
@@ -1464,7 +1464,7 @@
(let [src (fn []
(let [rng (java.util.Random. seed)]
(iteration #(unchecked-add % (.nextLong rng))
- :some? (complement #(zero? (mod % 1000)))
+ :somef (complement #(zero? (mod % 1000)))
:vf str
:initk initk)))]
(= (into [] (src))
From 326366d11923620f6815cfd149300bb48da01593 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Wed, 19 Jan 2022 12:55:12 -0600
Subject: [PATCH 095/285] CLJ-2689 Provide more tolerance for inexact floating
point math tests
Signed-off-by: Alex Miller
---
test/clojure/test_clojure/math.clj | 44 +++++++++++++++++-------------
1 file changed, 25 insertions(+), 19 deletions(-)
diff --git a/test/clojure/test_clojure/math.clj b/test/clojure/test_clojure/math.clj
index 63975e7505..4520b41b2b 100644
--- a/test/clojure/test_clojure/math.clj
+++ b/test/clojure/test_clojure/math.clj
@@ -21,20 +21,26 @@
[^double d]
(and (zero? d) (not (< (Double/compare d 0.0) 0))))
+(defn ulp=
+ "Tests that y = x +/- m*ulp(x)"
+ [x y ^double m]
+ (let [mu (* (m/ulp x) m)]
+ (<= (- x mu) y (+ x mu))))
+
(deftest test-sin
(is (NaN? (m/sin ##NaN)))
(is (NaN? (m/sin ##-Inf)))
(is (NaN? (m/sin ##Inf)))
(is (pos-zero? (m/sin 0.0)))
(is (neg-zero? (m/sin -0.0)))
- (is (= (m/sin m/PI) (- (m/sin (- m/PI))))))
+ (is (ulp= (m/sin m/PI) (- (m/sin (- m/PI))) 1)))
(deftest test-cos
(is (NaN? (m/cos ##NaN)))
(is (NaN? (m/cos ##-Inf)))
(is (NaN? (m/cos ##Inf)))
(is (= 1.0 (m/cos 0.0) (m/cos -0.0)))
- (is (= (m/cos m/PI) (m/cos (- m/PI)))))
+ (is (ulp= (m/cos m/PI) (m/cos (- m/PI)) 1)))
(deftest test-tan
(is (NaN? (m/tan ##NaN)))
@@ -42,7 +48,7 @@
(is (NaN? (m/tan ##Inf)))
(is (pos-zero? (m/tan 0.0)))
(is (neg-zero? (m/tan -0.0)))
- (is (= (- (m/tan m/PI)) (m/tan (- m/PI)))))
+ (is (ulp= (- (m/tan m/PI)) (m/tan (- m/PI)) 1)))
(deftest test-asin
(is (NaN? (m/asin ##NaN)))
@@ -54,45 +60,45 @@
(is (NaN? (m/acos ##NaN)))
(is (NaN? (m/acos -2.0)))
(is (NaN? (m/acos 2.0)))
- (is (= m/PI (* 2 (m/acos 0.0)))))
+ (is (ulp= (* 2 (m/acos 0.0)) m/PI 1)))
(deftest test-atan
(is (NaN? (m/atan ##NaN)))
(is (pos-zero? (m/atan 0.0)))
(is (neg-zero? (m/atan -0.0)))
- (is (= 0.7853981633974483 (m/atan 1))))
+ (is (ulp= (m/atan 1) 0.7853981633974483 1)))
(deftest test-radians-degrees-roundtrip
(doseq [d (range 0.0 360.0 5.0)]
- (is (= (m/round d) (m/round (-> d m/to-radians m/to-degrees))))))
+ (is (ulp= (m/round d) (m/round (-> d m/to-radians m/to-degrees)) 1))))
(deftest test-exp
(is (NaN? (m/exp ##NaN)))
(is (= ##Inf (m/exp ##Inf)))
(is (pos-zero? (m/exp ##-Inf)))
- (is (= 1.0 (m/exp 0.0)))
- (is (= m/E (m/exp 1))))
+ (is (ulp= (m/exp 0.0) 1.0 1))
+ (is (ulp= (m/exp 1) m/E 1)))
(deftest test-log
(is (NaN? (m/log ##NaN)))
(is (NaN? (m/log -1.0)))
(is (= ##Inf (m/log ##Inf)))
(is (= ##-Inf (m/log 0.0)))
- (is (= 1.0 (m/log m/E))))
+ (is (ulp= (m/log m/E) 1.0 1)))
(deftest test-log10
(is (NaN? (m/log10 ##NaN)))
(is (NaN? (m/log10 -1.0)))
(is (= ##Inf (m/log10 ##Inf)))
(is (= ##-Inf (m/log10 0.0)))
- (is (= 1.0 (m/log10 10))))
+ (is (ulp= (m/log10 10) 1.0 1)))
(deftest test-sqrt
(is (NaN? (m/sqrt ##NaN)))
(is (NaN? (m/sqrt -1.0)))
(is (= ##Inf (m/sqrt ##Inf)))
(is (pos-zero? (m/sqrt 0)))
- (is (= 2.0 (m/sqrt 4.0))))
+ (is (= (m/sqrt 4.0) 2.0)))
(deftest test-cbrt
(is (NaN? (m/cbrt ##NaN)))
@@ -133,14 +139,14 @@
(is (NaN? (m/atan2 1.0 ##NaN)))
(is (pos-zero? (m/atan2 0.0 1.0)))
(is (neg-zero? (m/atan2 -0.0 1.0)))
- (is (= m/PI (m/atan2 0.0 -1.0)))
- (is (= (- m/PI) (m/atan2 -0.0 -1.0)))
- (is (= m/PI (* 2.0 (m/atan2 1.0 0.0))))
- (is (= m/PI (* -2.0 (m/atan2 -1.0 0.0))))
- (is (= m/PI (* 4.0 (m/atan2 ##Inf ##Inf))))
- (is (= m/PI (/ (* 4.0 (m/atan2 ##Inf ##-Inf)) 3.0)))
- (is (= m/PI (* -4.0 (m/atan2 ##-Inf ##Inf))))
- (is (= m/PI (/ (* -4.0 (m/atan2 ##-Inf ##-Inf)) 3.0))))
+ (is (ulp= (m/atan2 0.0 -1.0) m/PI 2))
+ (is (ulp= (m/atan2 -0.0 -1.0) (- m/PI) 2))
+ (is (ulp= (* 2.0 (m/atan2 1.0 0.0)) m/PI 2))
+ (is (ulp= (* -2.0 (m/atan2 -1.0 0.0)) m/PI 2))
+ (is (ulp= (* 4.0 (m/atan2 ##Inf ##Inf)) m/PI 2))
+ (is (ulp= (/ (* 4.0 (m/atan2 ##Inf ##-Inf)) 3.0) m/PI 2))
+ (is (ulp= (* -4.0 (m/atan2 ##-Inf ##Inf)) m/PI 2))
+ (is (ulp= (/ (* -4.0 (m/atan2 ##-Inf ##-Inf)) 3.0) m/PI 2)))
(deftest test-pow
(is (= 1.0 (m/pow 4.0 0.0)))
From da0b9574017918deede6d2a15f386a7cc1b70a2c Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 20 Jan 2022 16:13:26 -0600
Subject: [PATCH 096/285] CLJ-2685 Fix iteration generative test comparison
Signed-off-by: Alex Miller
---
test/clojure/test_clojure/sequences.clj | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/test/clojure/test_clojure/sequences.clj b/test/clojure/test_clojure/sequences.clj
index 1fc93cd8ce..4eb99e7860 100644
--- a/test/clojure/test_clojure/sequences.clj
+++ b/test/clojure/test_clojure/sequences.clj
@@ -988,7 +988,7 @@
{} {:a 1 :b 2}
#{} #{1 2} ))
-(defspec longrange-equals-range 100
+(defspec longrange-equals-range 1000
(prop/for-all [start gen/int
end gen/int
step gen/s-pos-int]
@@ -1458,7 +1458,7 @@
:kf #(some-> % :k #{0 1 2})
:vf :item))))))
-(defspec iteration-seq-equals-reduce 100
+(defspec iteration-seq-equals-reduce 1000
(prop/for-all [initk gen/int
seed gen/int]
(let [src (fn []
@@ -1468,4 +1468,4 @@
:vf str
:initk initk)))]
(= (into [] (src))
- (seq (src))))))
\ No newline at end of file
+ (into [] (seq (src)))))))
From 027d8ff2859442b222bf9cfa4c1be45567b788eb Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Wed, 2 Feb 2022 14:43:41 -0600
Subject: [PATCH 097/285] Revert "CLJ-2493: Prevent browse-url from hanging
when using xdg-open (updated)"
This reverts commit c33a5abca7828843b7018a8aa28e63dd40310f86.
---
src/clj/clojure/java/browse.clj | 2 +-
src/clj/clojure/java/shell.clj | 20 --------------------
2 files changed, 1 insertion(+), 21 deletions(-)
diff --git a/src/clj/clojure/java/browse.clj b/src/clj/clojure/java/browse.clj
index c416e53832..6fcc650756 100644
--- a/src/clj/clojure/java/browse.clj
+++ b/src/clj/clojure/java/browse.clj
@@ -71,6 +71,6 @@
script (if (= :uninitialized script)
(reset! *open-url-script* (open-url-script-val))
script)]
- (or (when script (sh/launch script (str url)) true)
+ (or (when script (sh/sh script (str url)) true)
(open-url-in-browser url)
(open-url-in-swing url))))
diff --git a/src/clj/clojure/java/shell.clj b/src/clj/clojure/java/shell.clj
index 8f167086df..cb3f90a276 100644
--- a/src/clj/clojure/java/shell.clj
+++ b/src/clj/clojure/java/shell.clj
@@ -127,21 +127,6 @@ collecting its stdout"}
exit-code (.waitFor proc)]
{:exit exit-code :out @out :err @err}))))
-(defn launch
- "Same as sh except the I/O channels of the sub-process are ignored.
-
- Use when you want the sub-process to take an action, but do not want to wait
- for the I/O channels to close. Example: Launching a browser with xdg-open.
- "
- {:added "1.11"}
- [& args]
- (let [[cmd opts] (parse-args args)
- proc (.exec (Runtime/getRuntime)
- ^"[Ljava.lang.String;" (into-array cmd)
- (as-env-strings (:env opts))
- (as-file (:dir opts)))]
- {:exit (.waitFor proc)}))
-
(comment
(println (sh "ls" "-l"))
@@ -154,9 +139,4 @@ collecting its stdout"}
(println (sh "cat" "myimage.png" :out-enc :bytes)) ; reads binary file into bytes[]
(println (sh "cmd" "/c dir 1>&2"))
-(println (launch "touch" "my-test-file"))
-(println (sh "ls" "-l" "my-test-file"))
-(println (launch "rm" "my-test-file"))
-(println (sh "ls" "-l" "my-test-file"))
-
)
From 19e3a2708def5ffb7f2be030d8e8e895464ce2d2 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Tue, 25 Jan 2022 12:02:09 -0600
Subject: [PATCH 098/285] CLJ-2493 - Prevent browse-url from hanging when using
xdg-open
Signed-off-by: Alex Miller
---
src/clj/clojure/java/browse.clj | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/src/clj/clojure/java/browse.clj b/src/clj/clojure/java/browse.clj
index 6fcc650756..6a16ce37db 100644
--- a/src/clj/clojure/java/browse.clj
+++ b/src/clj/clojure/java/browse.clj
@@ -12,7 +12,9 @@
clojure.java.browse
(:require [clojure.java.shell :as sh]
[clojure.string :as str])
- (:import (java.net URI)))
+ (:import (java.io File)
+ (java.net URI)
+ (java.lang ProcessBuilder ProcessBuilder$Redirect)))
(defn- macosx? []
(-> "os.name" System/getProperty .toLowerCase
@@ -71,6 +73,16 @@
script (if (= :uninitialized script)
(reset! *open-url-script* (open-url-script-val))
script)]
- (or (when script (sh/sh script (str url)) true)
+ (or (when script
+ (try
+ (let [command [script (str url)]
+ null-file (File. (if (.startsWith (System/getProperty "os.name") "Windows") "NUL" "/dev/null"))
+ pb (doto (ProcessBuilder. ^java.util.List command)
+ ;; emulate ProcessBuilder.Redirect.DISCARD added in Java 9
+ (.redirectOutput null-file)
+ (.redirectError null-file))]
+ (.start pb) ;; do not wait for the process
+ true)
+ (catch Throwable _ false)))
(open-url-in-browser url)
(open-url-in-swing url))))
From 4a9ce390519c41eae100954378937eb220a81dc0 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Tue, 25 Jan 2022 21:03:58 -0600
Subject: [PATCH 099/285] CLJ-2529 Set exception phase for runtime exceptions
caught in Compiler.load()
Signed-off-by: Alex Miller
---
src/jvm/clojure/lang/Compiler.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java
index cacaeb0f0e..12cf1e5623 100644
--- a/src/jvm/clojure/lang/Compiler.java
+++ b/src/jvm/clojure/lang/Compiler.java
@@ -7662,7 +7662,7 @@ public static Object load(Reader rdr, String sourcePath, String sourceName) {
catch(Throwable e)
{
if(!(e instanceof CompilerException))
- throw new CompilerException(sourcePath, (Integer) LINE_BEFORE.deref(), (Integer) COLUMN_BEFORE.deref(), e);
+ throw new CompilerException(sourcePath, (Integer) LINE_BEFORE.deref(), (Integer) COLUMN_BEFORE.deref(), null, CompilerException.PHASE_EXECUTION, e);
else
throw (CompilerException) e;
}
From fc98f92c76254c5a6306debaf0f9df28c3bb3646 Mon Sep 17 00:00:00 2001
From: Fogus
Date: Tue, 1 Feb 2022 11:34:18 -0500
Subject: [PATCH 100/285] CLJ-2620: io-prepl now calls valf on exeception data
in the case where an exception occurs in the :print-eval-result phase
Signed-off-by: Alex Miller
---
src/clj/clojure/core/server.clj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/clj/clojure/core/server.clj b/src/clj/clojure/core/server.clj
index 48377d4127..3cda4d1f74 100644
--- a/src/clj/clojure/core/server.clj
+++ b/src/clj/clojure/core/server.clj
@@ -288,7 +288,7 @@
(try
(assoc m :val (valf (:val m)))
(catch Throwable ex
- (assoc m :val (ex->data ex :print-eval-result)
+ (assoc m :val (valf (ex->data ex :print-eval-result))
:exception true)))
m))))))))
From 8957a93099fc506c3b24ed5739bf9e2fc1811bef Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 20 Jan 2022 21:18:39 -0600
Subject: [PATCH 101/285] CLJ-1180 Resolve classname for tag metadata on
defprotocol
Signed-off-by: Alex Miller
---
src/clj/clojure/core_deftype.clj | 10 +++++-
test/clojure/test_clojure/protocols.clj | 35 +++++++++++++++----
.../test_clojure/protocols/examples.clj | 2 ++
3 files changed, 40 insertions(+), 7 deletions(-)
diff --git a/src/clj/clojure/core_deftype.clj b/src/clj/clojure/core_deftype.clj
index 7906ccbe35..c2babab291 100644
--- a/src/clj/clojure/core_deftype.clj
+++ b/src/clj/clojure/core_deftype.clj
@@ -652,7 +652,15 @@
[opts sigs]))
sigs (when sigs
(reduce1 (fn [m s]
- (let [name-meta (meta (first s))
+ (let [tag-to-class (fn [tag]
+ (if-let [c (and (instance? clojure.lang.Symbol tag)
+ (= (.indexOf (.getName ^clojure.lang.Symbol tag) ".") -1)
+ (not (contains? '#{int long float double char short byte boolean void
+ ints longs floats doubles chars shorts bytes booleans objects} tag))
+ (resolve tag))]
+ (symbol (.getName c))
+ tag))
+ name-meta (update-in (meta (first s)) [:tag] tag-to-class)
mname (with-meta (first s) nil)
[arglists doc]
(loop [as [] rs (rest s)]
diff --git a/test/clojure/test_clojure/protocols.clj b/test/clojure/test_clojure/protocols.clj
index ff556dcaed..4e951449bb 100644
--- a/test/clojure/test_clojure/protocols.clj
+++ b/test/clojure/test_clojure/protocols.clj
@@ -47,13 +47,13 @@
(deftest protocols-test
(testing "protocol fns have useful metadata"
(let [common-meta {:ns (find-ns 'clojure.test-clojure.protocols.examples)
- :protocol #'ExampleProtocol}]
- (are [m f] (= (merge (quote m) common-meta)
+ :protocol #'ExampleProtocol :tag nil}]
+ (are [m f] (= (merge common-meta m)
(meta (var f)))
- {:name foo :arglists ([a]) :doc "method with one arg"} foo
- {:name bar :arglists ([a b]) :doc "method with two args"} bar
- {:name baz :arglists ([a] [a b]) :doc "method with multiple arities" :tag String} baz
- {:name with-quux :arglists ([a]) :doc "method name with a hyphen"} with-quux)))
+ {:name 'foo :arglists '([a]) :doc "method with one arg"} foo
+ {:name 'bar :arglists '([a b]) :doc "method with two args"} bar
+ {:name 'baz :arglists '([a] [a b]) :doc "method with multiple arities" :tag 'java.lang.String} baz
+ {:name 'with-quux :arglists '([a]) :doc "method name with a hyphen"} with-quux)))
(testing "protocol fns throw IllegalArgumentException if no impl matches"
(is (thrown-with-msg?
IllegalArgumentException
@@ -682,3 +682,26 @@
(reduce-kv #(assoc %1 %3 %2)
{}
(seq {:a 1 :b 2})))))
+
+(defn aget-long-hinted ^long [x] (aget (longs-hinted x) 0))
+
+(deftest test-longs-hinted-proto
+ (is (= 1
+ (aget-long-hinted
+ (reify LongsHintedProto
+ (longs-hinted [_] (long-array [1])))))))
+
+;; CLJ-1180 - resolve type hints in protocol methods
+
+(import 'clojure.lang.ISeq)
+(defprotocol P
+ (^ISeq f [_]))
+(ns clojure.test-clojure.protocols.other
+ (:use clojure.test))
+(defn cf [val]
+ (let [aseq (clojure.test-clojure.protocols/f val)]
+ (count aseq)))
+(extend-protocol clojure.test-clojure.protocols/P String
+ (f [s] (seq s)))
+(deftest test-resolve-type-hints-in-protocol-methods
+ (is (= 4 (clojure.test-clojure.protocols/f "test"))))
diff --git a/test/clojure/test_clojure/protocols/examples.clj b/test/clojure/test_clojure/protocols/examples.clj
index 9d962d5651..0c247eab04 100644
--- a/test/clojure/test_clojure/protocols/examples.clj
+++ b/test/clojure/test_clojure/protocols/examples.clj
@@ -17,3 +17,5 @@
(hinted [^int i])
(hinted [^String s]))
+(defprotocol LongsHintedProto
+ (^longs longs-hinted [_]))
From 658693f6cf97e6ab0ff789e096c9eb6654e4d3ab Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant
Date: Tue, 25 Jan 2022 12:34:24 -0600
Subject: [PATCH 102/285] CLJ-1973: sort proxy methods for reproducibility
Comparator sorts methods by arity then name/param-types/return-types on clashes.
Signed-off-by: Alex Miller
---
build.xml | 1 +
src/clj/clojure/core_proxy.clj | 15 +++++---
test/clojure/test_clojure/java_interop.clj | 38 ++++++++++++++++++--
test/clojure/test_clojure/proxy/examples.clj | 30 ++++++++++++++++
4 files changed, 77 insertions(+), 7 deletions(-)
create mode 100644 test/clojure/test_clojure/proxy/examples.clj
diff --git a/build.xml b/build.xml
index 6796b3d986..34fd65b2c6 100644
--- a/build.xml
+++ b/build.xml
@@ -105,6 +105,7 @@
+
diff --git a/src/clj/clojure/core_proxy.clj b/src/clj/clojure/core_proxy.clj
index c7f35774e6..46f7b4b868 100644
--- a/src/clj/clojure/core_proxy.clj
+++ b/src/clj/clojure/core_proxy.clj
@@ -241,12 +241,17 @@
mb (map #(vector (%1 %2) (vals (dissoc %1 %2))) mgroups rtypes)
bridge? (reduce1 into1 #{} (map second mb))
ifaces-meths (remove bridge? (vals ifaces-meths))
- mm (remove bridge? (vals mm))]
+ mm (remove bridge? (vals mm))
+ reflect-Method-keyfn (fn [meth]
+ (let [[name param-types ^Class return-type] (method-sig meth)]
+ (-> [name]
+ (into1 (map #(.getName ^Class %) param-types))
+ (conj (.getName return-type)))))]
;add methods matching supers', if no mapping -> call super
- (doseq [[^java.lang.reflect.Method dest bridges] mb
- ^java.lang.reflect.Method meth bridges]
+ (doseq [[^java.lang.reflect.Method dest bridges] (sort-by (comp reflect-Method-keyfn first) mb)
+ ^java.lang.reflect.Method meth (sort-by reflect-Method-keyfn bridges)]
(gen-bridge meth dest))
- (doseq [^java.lang.reflect.Method meth mm]
+ (doseq [^java.lang.reflect.Method meth (sort-by reflect-Method-keyfn mm)]
(gen-method meth
(fn [^GeneratorAdapter gen ^Method m]
(. gen (loadThis))
@@ -259,7 +264,7 @@
(. m (getDescriptor)))))))
;add methods matching interfaces', if no mapping -> throw
- (doseq [^java.lang.reflect.Method meth ifaces-meths]
+ (doseq [^java.lang.reflect.Method meth (sort-by reflect-Method-keyfn ifaces-meths)]
(gen-method meth
(fn [^GeneratorAdapter gen ^Method m]
(. gen (throwException ex-type (. m (getName))))))))
diff --git a/test/clojure/test_clojure/java_interop.clj b/test/clojure/test_clojure/java_interop.clj
index cb21064b25..4925284cc2 100644
--- a/test/clojure/test_clojure/java_interop.clj
+++ b/test/clojure/test_clojure/java_interop.clj
@@ -11,8 +11,11 @@
(ns clojure.test-clojure.java-interop
(:use clojure.test)
- (:require [clojure.inspector]
- [clojure.set :as set])
+ (:require [clojure.data :as data]
+ [clojure.inspector]
+ [clojure.pprint :as pp]
+ [clojure.set :as set]
+ [clojure.test-clojure.proxy.examples :as proxy-examples])
(:import java.util.Base64
(java.util.concurrent.atomic AtomicLong AtomicInteger)))
@@ -176,6 +179,37 @@
str)
"chain chain chain")))
+;https://clojure.atlassian.net/browse/CLJ-1973
+(deftest test-proxy-method-order
+ (let [class-reader (clojure.asm.ClassReader. proxy-examples/proxy1-class-name)
+ method-order (atom [])
+ method-visitor (proxy [clojure.asm.ClassVisitor] [clojure.asm.Opcodes/ASM4 nil]
+ (visitMethod [access name descriptor signature exceptions]
+ (swap! method-order conj {:name name :descriptor descriptor})
+ nil))
+ _ (.accept class-reader method-visitor 0)
+ expected [{:name "", :descriptor "()V"}
+ {:name "__initClojureFnMappings", :descriptor "(Lclojure/lang/IPersistentMap;)V"}
+ {:name "__updateClojureFnMappings", :descriptor "(Lclojure/lang/IPersistentMap;)V"}
+ {:name "__getClojureFnMappings", :descriptor "()Lclojure/lang/IPersistentMap;"}
+ {:name "clone", :descriptor "()Ljava/lang/Object;"}
+ {:name "hashCode", :descriptor "()I"}
+ {:name "toString", :descriptor "()Ljava/lang/String;"}
+ {:name "equals", :descriptor "(Ljava/lang/Object;)Z"}
+ {:name "a", :descriptor "(Ljava/io/File;)Z"}
+ {:name "a", :descriptor "(Ljava/lang/Boolean;)Ljava/lang/Object;"}
+ {:name "a", :descriptor "(Ljava/lang/Runnable;)Z"}
+ {:name "a", :descriptor "(Ljava/lang/String;)I"}
+ {:name "b", :descriptor "(Ljava/lang/String;)Ljava/lang/Object;"}
+ {:name "c", :descriptor "(Ljava/lang/String;)Ljava/lang/Object;"}
+ {:name "d", :descriptor "(Ljava/lang/String;)Ljava/lang/Object;"}
+ {:name "a", :descriptor "(Ljava/lang/Boolean;Ljava/lang/String;)I"}
+ {:name "a", :descriptor "(Ljava/lang/String;Ljava/io/File;)Z"}
+ {:name "a", :descriptor "(Ljava/lang/String;Ljava/lang/Runnable;)Z"}
+ {:name "a", :descriptor "(Ljava/lang/String;Ljava/lang/String;)I"}]
+ actual @method-order]
+ (is (= expected actual)
+ (with-out-str (pp/pprint (data/diff expected actual))))))
;; serialized-proxy can be regenerated using a modified version of
;; Clojure with the proxy serialization prohibition disabled and the
diff --git a/test/clojure/test_clojure/proxy/examples.clj b/test/clojure/test_clojure/proxy/examples.clj
new file mode 100644
index 0000000000..b98042320f
--- /dev/null
+++ b/test/clojure/test_clojure/proxy/examples.clj
@@ -0,0 +1,30 @@
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html at the root of this distribution.
+; By using this software in any fashion, you are agreeing to be bound by
+; the terms of this license.
+; You must not remove this notice, or any other, from this software.
+
+(ns ^{:doc "Test proxy classes that are AOT-compiled for the tests in
+ clojure.test-clojure.java-interop."
+ :author "Ambrose Bonnaire-Sergeant"}
+ clojure.test-clojure.proxy.examples)
+
+(definterface A
+ (^int a [^String x])
+ (^boolean a [^java.io.File x])
+ (^boolean a [^Runnable x])
+ (a [^Boolean x])
+ (^int a [^Boolean x ^String y])
+ (^int a [^String x ^String y])
+ (^boolean a [^String x ^java.io.File y])
+ (^boolean a [^String x ^Runnable y])
+ (b [^String x])
+ (c [^String x])
+ (d [^String x]))
+
+(def ^String proxy1-class-name
+ (-> (proxy [A] [])
+ class
+ .getName))
From 16c91f256fd843687be99b4e3fe2e9af12f5428a Mon Sep 17 00:00:00 2001
From: Clojure Build
Date: Wed, 2 Feb 2022 15:07:32 -0600
Subject: [PATCH 103/285] [maven-release-plugin] prepare release
clojure-1.11.0-beta1
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 59159805a1..4be09160a6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.11.0-master-SNAPSHOT
+ 1.11.0-beta1
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- HEAD
+ clojure-1.11.0-beta1
From 84811650bb33846c7212f391468f9661819b906b Mon Sep 17 00:00:00 2001
From: Clojure Build
Date: Wed, 2 Feb 2022 15:07:32 -0600
Subject: [PATCH 104/285] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 4be09160a6..59159805a1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.11.0-beta1
+ 1.11.0-master-SNAPSHOT
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- clojure-1.11.0-beta1
+ HEAD
From 3fbdffadabbc36436525a0183f2b3c8aca8648a3 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Tue, 15 Feb 2022 10:52:35 -0600
Subject: [PATCH 105/285] Changelog updates for 1.11
Signed-off-by: Alex Miller
---
changes.md | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 159 insertions(+)
diff --git a/changes.md b/changes.md
index 4df7e28540..46b45cbda1 100644
--- a/changes.md
+++ b/changes.md
@@ -1,5 +1,164 @@
+# Changes to Clojure in Version 1.11.0
+
+## 1 Compatibility
+
+### 1.1 Security
+
+Because XML external entity (XXE) attacks can be used to disclose local files using file schemes or relative paths in the system identifier, `clojure.xml/parse` now disables external entity processing by default.
+
+See: https://owasp.org/www-community/vulnerabilities/XML_External_Entity_(XXE)_Processing
+
+This change disables the following SAX parser features:
+
+* `http://apache.org/xml/features/nonvalidating/load-external-dtd`
+* `http://xml.org/sax/features/external-general-entities`
+* `http://xml.org/sax/features/external-parameter-entities`
+
+If you rely on these features, modify your calls to `clojure.xml/parse` to explicitly
+supply `startparse-sax` function as the final argument:
+`(clojure.xml/parse the-string clojure.xml/startparse-sax)`
+This modification also works on prior Clojure versions.
+
+* [CLJ-2611](http://dev.clojure.org/jira/browse/CLJ-2611) clojure.xml now disables XXE processing by default
+
+### 1.2 Dependencies
+
+Updated dependencies:
+
+* spec.alpha dependency to 0.3.218 - [changes](https://github.com/clojure/spec.alpha/blob/master/CHANGES.md)
+* core.specs.alpha dependency to 0.2.62 - [changes](https://github.com/clojure/core.specs.alpha/blob/master/CHANGES.md)
+
+## 2 Features
+
+### 2.1 Keyword argument functions take a trailing map
+
+Keyword arguments are optional trailing variadic arguments of the form *akey aval bkey bval...​*.
+In Clojure 1.11, functions taking keyword arguments can now be passed a map instead of or in addition
+to and following the key/value pairs. When a lone map is passed, it is used for destructuring, else
+a trailing map is added to the key/value pair map by `conj`.
+
+Also see: https://clojure.org/news/2021/03/18/apis-serving-people-and-programs
+
+* [CLJ-2603](https://clojure.atlassian.net/browse/CLJ-2603) Clojure keyword argument functions now also accept a map
+
+### 2.2 `:as-alias` in `require`
+
+Spec (and other libs) rely on qualified keywords as spec names.
+Namespace aliasing in `ns` makes long names shorter but required namespaces to be loadable.
+This change adds `:as-alias` to `require`, which is like `:as` but does not require the namespace to load.
+
+* [CLJ-2123](https://clojure.atlassian.net/browse/CLJ-2123) Add :as-alias option to require like :as but not load
+* [CLJ-2665](https://clojure.atlassian.net/browse/CLJ-2665) Fix require with :as and :as-alias to load
+
+## 3 New functions and namespaces
+
+### 3.1 clojure.math and numeric helper functions
+
+Added a new clojure.math namespace which provides wrappers for the functions available in java.lang.Math.
+These functions are narrowed to only `long` and `double` overloads and provide primitive support without reflection.
+
+In addition, the following functions were added to clojure.core:
+
+* `abs` - absolute value in optimized form for all Clojure numeric types (long, double, ratio, bigint, bigdecimal)
+* `NaN?` - predicate for doubles to check whether "not a number"
+* `infinite?` - predicate for doubles to check whether positive or negative infinity
+
+* [CLJ-2668](https://clojure.atlassian.net/browse/CLJ-2668) Add NaN? and infinite? predicates
+* [CLJ-2664](https://clojure.atlassian.net/browse/CLJ-2664) Add clojure.java.math namespace, wrappers for java.lang.Math
+* [CLJ-2673](https://clojure.atlassian.net/browse/CLJ-2673) Add `abs`, and update `min` and `max` to use Math impls when possible
+* [CLJ-2677](https://clojure.atlassian.net/browse/CLJ-2677) clojure.math - fix method reflection in bodies and inlines, fix docstrings, renamed
+* [CLJ-2689](https://clojure.atlassian.net/browse/CLJ-2689) Fix clojure.math tests to be more tolerant of floating point comparisons
+
+### 3.2 Parser functions
+
+Added the following parsing functions to clojure.core:
+
+* `parse-double` - parses floating point number, including scientific notation
+* `parse-long` - parses integer in long range
+* `parse-boolean` - parses `"true"` or `"false"` to the canonical boolean values
+* `parse-uuid` - parses a UUID string to java.util.UUID
+
+All of these functions expect a string argument and return either the parsed value or `nil` if the value
+is in invalid format.
+
+* [CLJ-2667](https://clojure.atlassian.net/browse/CLJ-2667) Add functions to parse a single long/double/uuid/boolean from a string
+
+### 3.2 `random-uuid`
+
+Added `random-uuid`, a function to construct a random java.util.UUID.
+
+* [CLJ-1925](https://clojure.atlassian.net/browse/CLJ-1925) Add random-uuid
+
+### 3.3 `update-keys` and `update-vals`
+
+Added:
+
+* `update-keys` - applies a function to every key in a map, `m f => {(f k) v ...}`
+* `update-vals` - applies a function to every value in a map, `m f => {k (f v) ...}`
+
+* [CLJ-1959](https://clojure.atlassian.net/browse/CLJ-1959) Add implementation of update-keys
+* [CLJ-2651](https://clojure.atlassian.net/browse/CLJ-2651) Add implementation of update-vals
+
+### 3.4 `iteration`
+
+Added `iteration`, to repeatedly apply a (possibly impure) step function with continuation state.
+This can be used e.g. to consume APIs that return paginated or batched data.
+
+* [CLJ-2555](https://clojure.atlassian.net/browse/CLJ-2555) Add `iteration` generator function
+* [CLJ-2690](https://clojure.atlassian.net/browse/CLJ-2690) Improve `iteration` docstring and arg names
+* [CLJ-2685](https://clojure.atlassian.net/browse/CLJ-2685) Fix `iteration` generative test failure
+
+## 4 Fixes
+
+### 4.1 Compiler
+
+* [CLJ-2680](https://clojure.atlassian.net/browse/CLJ-2680) Fix type hinting a primitive local with matching type hint to not error
+* [CLJ-1180](https://clojure.atlassian.net/browse/CLJ-1180) Fix resolution of class type hints in `defprotocol`
+* [CLJ-1973](https://clojure.atlassian.net/browse/CLJ-1973) Make order of emitted protocol methods in generated classes reproducible
+
+### 4.2 Core
+
+* [CLJ-1879](https://clojure.atlassian.net/browse/CLJ-1879) IKVReduce - make IPersistentMap case faster and extend to Object, detaching it from any fully enumerable set of types
+* [CLJ-2065](https://clojure.atlassian.net/browse/CLJ-2065) IKVReduce - add direct support for SubVector
+* [CLJ-2663](https://clojure.atlassian.net/browse/CLJ-2663) Fix vector `=` not terminating when called with infinite sequence
+* [CLJ-2679](https://clojure.atlassian.net/browse/CLJ-2679) Fix hash collisions in `case` expressions on symbols
+* [CLJ-2600](https://clojure.atlassian.net/browse/CLJ-2600) Don't block `realized?` of `delay` on pending result
+* [CLJ-2649](https://clojure.atlassian.net/browse/CLJ-2649) Fix order of checks in `some-fn` and `every-pred` for 3 predicate case to match other unrollings
+* [CLJ-2234](https://clojure.atlassian.net/browse/CLJ-2234) Fix multimethod preferences to correctly use local hierarchy when it exists
+* [CLJ-2556](https://clojure.atlassian.net/browse/CLJ-2556) Fix `into` completion so `halt-when` works
+
+### 4.3 Performance
+
+* [CLJ-1808](https://clojure.atlassian.net/browse/CLJ-1808) `map-invert` should use `reduce-kv` and transient
+* [CLJ-2621](https://clojure.atlassian.net/browse/CLJ-2621) Fix unnecessary boxing of unused return in statement context for instance method expr
+* [CLJ-2670](https://clojure.atlassian.net/browse/CLJ-2670) Use Math.exact... methods for checked long math ops for performance
+* [CLJ-2636](https://clojure.atlassian.net/browse/CLJ-2636) Get rid of reflection on java.util.Properties when defining `*clojure-version*`
+* [CLJ-1509](https://clojure.atlassian.net/browse/CLJ-1509) AOT compile clojure.instant, clojure.uuid, clojure.core.reducers in build
+
+### 4.4 Error messages
+
+* [CLJ-2529](https://clojure.atlassian.net/browse/CLJ-2529) Fix incorrect reporting of runtime errors as compiler errors in calls through `Compiler.load()`
+* [CLJ-2350](https://clojure.atlassian.net/browse/CLJ-2350) Improve keyword arity exception message
+
+### 4.5 Docstrings
+
+* [CLJ-2249](https://clojure.atlassian.net/browse/CLJ-2249) Clarify `get` docstring regarding sets, strings, arrays, ILookup
+* [CLJ-2488](https://clojure.atlassian.net/browse/CLJ-2488) Add definition to `reify` docstring
+* [CLJ-1360](https://clojure.atlassian.net/browse/CLJ-1360) Update `clojure.string/split` docstring regarding trailing empty parts
+* [CLJ-2444](https://clojure.atlassian.net/browse/CLJ-2444) Fix typo in `test-vars` docstring
+* [CLJ-2666](https://clojure.atlassian.net/browse/CLJ-2666) Make Clojure Java API javadoc text match the example
+
+### 4.6 Other enhancements
+
+* [CLJ-2493](https://clojure.atlassian.net/browse/CLJ-2493) clojure.java.browse - Fix `browse-url` hanging on call to xdg-open
+* [CLJ-1908](https://clojure.atlassian.net/browse/CLJ-1908) clojure.test - Add `run-test` and `run-test-var` to run single test with fixtures and report
+* [CLJ-1379](https://clojure.atlassian.net/browse/CLJ-1379) clojure.test - Fix quoting of `:actual` form in `:pass` maps
+* [CLJ-2620](https://clojure.atlassian.net/browse/CLJ-2620) clojure.server - Fix asymmetric handling of `:exception` `:val`s in `prepl`
+* [CLJ-2387](https://clojure.atlassian.net/browse/CLJ-2387) clojure.server - Fix off-by-one in socket server port validation
+
+
# Changes to Clojure in Version 1.10.3
## 1 Changes reverted
From 7b102d84c60ed88d62989d6fd4a6994bd33b3bec Mon Sep 17 00:00:00 2001
From: Paula Gearon
Date: Sat, 5 Feb 2022 15:14:01 -0500
Subject: [PATCH 106/285] CLJ-2695: parse-double test does not test out-of
range values
Signed-off-by: Alex Miller
---
test/clojure/test_clojure/parse.clj | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/test/clojure/test_clojure/parse.clj b/test/clojure/test_clojure/parse.clj
index 953beefb4d..8078c527f6 100644
--- a/test/clojure/test_clojure/parse.clj
+++ b/test/clojure/test_clojure/parse.clj
@@ -52,12 +52,15 @@
"Infinity" Double/POSITIVE_INFINITY
"-Infinity" Double/NEGATIVE_INFINITY
"1.7976931348623157E308" Double/MAX_VALUE
- "4.9E-324" Double/MIN_VALUE)
+ "4.9E-324" Double/MIN_VALUE
+ "1.7976931348623157E309" Double/POSITIVE_INFINITY ;; past max double
+ "2.5e-324" Double/MIN_VALUE ;; past min double, above half minimum
+ "2.4e-324" 0.0) ;; below minimum double
(is (Double/isNaN (parse-double "NaN")))
(are [s] ;; nil on invalid string
- (nil? (parse-long s))
- "1.7976931348623157E309" ;; past max double
- "9.9E-324")) ;; past min double
+ (nil? (parse-double s))
+ "double" ;; invalid string
+ "1.7976931348623157G309")) ;; invalid, but similar to valid
;; generative test - gen double -> str -> parse, compare
(deftest test-gen-parse-double
@@ -96,4 +99,4 @@
nil
false
true
- 100))
\ No newline at end of file
+ 100))
From 0971ce23f4cb3a1042483c56a36e4954da26ca70 Mon Sep 17 00:00:00 2001
From: Clojure Build
Date: Wed, 16 Feb 2022 10:22:29 -0600
Subject: [PATCH 107/285] [maven-release-plugin] prepare release
clojure-1.11.0-rc1
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 59159805a1..dc91d0a4aa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.11.0-master-SNAPSHOT
+ 1.11.0-rc1
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- HEAD
+ clojure-1.11.0-rc1
From 9af0d1d9a0dc34c406c3588dfe9b60dbe4530981 Mon Sep 17 00:00:00 2001
From: Clojure Build
Date: Wed, 16 Feb 2022 10:22:29 -0600
Subject: [PATCH 108/285] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index dc91d0a4aa..59159805a1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.11.0-rc1
+ 1.11.0-master-SNAPSHOT
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- clojure-1.11.0-rc1
+ HEAD
From f376cf62bb0c30f72b0df4ee94c38fa503fa4be7 Mon Sep 17 00:00:00 2001
From: Clojure Build
Date: Tue, 22 Mar 2022 10:16:08 -0500
Subject: [PATCH 109/285] [maven-release-plugin] prepare release clojure-1.11.0
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 59159805a1..e237f52448 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.11.0-master-SNAPSHOT
+ 1.11.0
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- HEAD
+ clojure-1.11.0
From 48818bd96afc2c8ee8ee76075f7a186289c6517a Mon Sep 17 00:00:00 2001
From: Clojure Build
Date: Tue, 22 Mar 2022 10:16:08 -0500
Subject: [PATCH 110/285] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index e237f52448..2dd37faaad 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.11.0
+ 1.12.0-master-SNAPSHOT
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- clojure-1.11.0
+ HEAD
From 1da6b07722790e898faa261a816a7a070fd93ac5 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Wed, 30 Mar 2022 15:41:32 -0500
Subject: [PATCH 111/285] CLJ-2701 Set serialVersionUIDs for Keyword and
ArraySeq to retain 1.10.3 values
Signed-off-by: Alex Miller
---
src/jvm/clojure/lang/ArraySeq.java | 3 +++
src/jvm/clojure/lang/Keyword.java | 2 ++
2 files changed, 5 insertions(+)
diff --git a/src/jvm/clojure/lang/ArraySeq.java b/src/jvm/clojure/lang/ArraySeq.java
index 49a74b660d..5f54d87aaf 100644
--- a/src/jvm/clojure/lang/ArraySeq.java
+++ b/src/jvm/clojure/lang/ArraySeq.java
@@ -15,6 +15,9 @@
import java.lang.reflect.Array;
public class ArraySeq extends ASeq implements IndexedSeq, IReduce{
+
+private static final long serialVersionUID = -9069152683729302290L;
+
public final Object[] array;
final int i;
//ISeq _rest;
diff --git a/src/jvm/clojure/lang/Keyword.java b/src/jvm/clojure/lang/Keyword.java
index 145df4f73b..253f455e4a 100644
--- a/src/jvm/clojure/lang/Keyword.java
+++ b/src/jvm/clojure/lang/Keyword.java
@@ -23,6 +23,8 @@
public class Keyword implements IFn, Comparable, Named, Serializable, IHashEq {
+private static final long serialVersionUID = -2105088845257724163L;
+
private static ConcurrentHashMap> table = new ConcurrentHashMap();
static final ReferenceQueue rq = new ReferenceQueue();
public final Symbol sym;
From e917dcc003bdd56660d5057407ea7739d2736af2 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Fri, 1 Apr 2022 12:46:31 -0500
Subject: [PATCH 112/285] Update changelog for 1.11.1
---
changes.md | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/changes.md b/changes.md
index 46b45cbda1..f18b330f4a 100644
--- a/changes.md
+++ b/changes.md
@@ -1,5 +1,10 @@
+# Changes to Clojure in Version 1.11.1
+
+* [CLJ-2701](https://clojure.atlassian.net/browse/CLJ-2701)
+ Pin serialVersionUID for Keyword and ArraySeq back to 1.10.3 values to retain binary serialization
+
# Changes to Clojure in Version 1.11.0
## 1 Compatibility
From 3f1c36d779a43b1951ddaa9cdf6250b1d739621b Mon Sep 17 00:00:00 2001
From: Clojure Build
Date: Fri, 1 Apr 2022 12:52:28 -0500
Subject: [PATCH 113/285] [maven-release-plugin] prepare release
clojure-1.11.1-rc1
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 2dd37faaad..4496e1353f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.12.0-master-SNAPSHOT
+ 1.11.1-rc1
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- HEAD
+ clojure-1.11.1-rc1
From cef38abac0d7139f4d38324290eaf2c40b8924a7 Mon Sep 17 00:00:00 2001
From: Clojure Build
Date: Fri, 1 Apr 2022 12:52:28 -0500
Subject: [PATCH 114/285] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 4496e1353f..2dd37faaad 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.11.1-rc1
+ 1.12.0-master-SNAPSHOT
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- clojure-1.11.1-rc1
+ HEAD
From ce55092f2b2f5481d25cff6205470c1335760ef6 Mon Sep 17 00:00:00 2001
From: Clojure Build
Date: Tue, 5 Apr 2022 09:33:41 -0500
Subject: [PATCH 115/285] [maven-release-plugin] prepare release clojure-1.11.1
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 2dd37faaad..b15f640224 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.12.0-master-SNAPSHOT
+ 1.11.1
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- HEAD
+ clojure-1.11.1
From 35bd89f05f8dc4aec47001ca10fe9163abc02ea6 Mon Sep 17 00:00:00 2001
From: Clojure Build
Date: Tue, 5 Apr 2022 09:33:41 -0500
Subject: [PATCH 116/285] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index b15f640224..2dd37faaad 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.11.1
+ 1.12.0-master-SNAPSHOT
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- clojure-1.11.1
+ HEAD
From b2366fa5c748f9d600879c3e0b549e631a5b386f Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Tue, 21 Jun 2022 11:03:57 -0500
Subject: [PATCH 117/285] CLJ-2713 Efficient drop, partition for
persistent/algo colls
Added new IDrop interface and implemented in PV, PAM, LongRange, Repeat,
StringSeq. Added new core functions that make vector partitions and make
better use of fast drop support - partitionv, partitionv-all, splitv-at.
---
src/clj/clojure/core.clj | 68 +++++-
src/jvm/clojure/lang/IDrop.java | 30 +++
src/jvm/clojure/lang/LongRange.java | 217 ++++++-------------
src/jvm/clojure/lang/PersistentArrayMap.java | 52 ++++-
src/jvm/clojure/lang/PersistentVector.java | 82 ++++++-
src/jvm/clojure/lang/Repeat.java | 12 +-
src/jvm/clojure/lang/StringSeq.java | 25 ++-
test/clojure/test_clojure/sequences.clj | 59 +++++
8 files changed, 383 insertions(+), 162 deletions(-)
create mode 100644 src/jvm/clojure/lang/IDrop.java
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 42abb7f00a..db5641339d 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -2924,7 +2924,7 @@
(cons (first s) (take-while pred (rest s))))))))
(defn drop
- "Returns a lazy sequence of all but the first n items in coll.
+ "Returns a laziness-preserving sequence of all but the first n items in coll.
Returns a stateful transducer when no collection is provided."
{:added "1.0"
:static true}
@@ -2941,12 +2941,14 @@
result
(rf result input))))))))
([n coll]
- (let [step (fn [n coll]
- (let [s (seq coll)]
- (if (and (pos? n) s)
- (recur (dec n) (rest s))
- s)))]
- (lazy-seq (step n coll)))))
+ (if (instance? clojure.lang.IDrop coll)
+ (or (.drop ^clojure.lang.IDrop coll n) ())
+ (let [step (fn [n coll]
+ (let [s (seq coll)]
+ (if (and (pos? n) s)
+ (recur (dec n) (rest s))
+ s)))]
+ (lazy-seq (step n coll))))))
(defn drop-last
"Return a lazy sequence of all but the last n (default 1) items in coll"
@@ -3167,20 +3169,24 @@
{:added "1.0"
:static true}
[coll n]
+ (if (instance? clojure.lang.IDrop coll)
+ (.drop ^clojure.lang.IDrop coll n)
(loop [n n xs (seq coll)]
(if (and xs (pos? n))
(recur (dec n) (next xs))
- xs)))
+ xs))))
(defn nthrest
"Returns the nth rest of coll, coll when n is 0."
{:added "1.3"
:static true}
[coll n]
+ (if (instance? clojure.lang.IDrop coll)
+ (or (.drop ^clojure.lang.IDrop coll n) ())
(loop [n n xs coll]
(if-let [xs (and (pos? n) (seq xs))]
(recur (dec n) (rest xs))
- xs)))
+ xs))))
(defn partition
"Returns a lazy sequence of lists of n items each, at offsets step
@@ -7339,6 +7345,50 @@ fails, attempts to require sym's namespace and retries."
(let [seg (doall (take n s))]
(cons seg (partition-all n step (nthrest s step))))))))
+(defn splitv-at
+ "Returns a vector of [(into [] (take n) coll) (drop n coll)]"
+ {:added "1.12"}
+ [n coll]
+ [(into [] (take n) coll) (drop n coll)])
+
+(defn partitionv
+ "Returns a lazy sequence of vectors of n items each, at offsets step
+ apart. If step is not supplied, defaults to n, i.e. the partitions
+ do not overlap. If a pad collection is supplied, use its elements as
+ necessary to complete last partition upto n items. In case there are
+ not enough padding elements, return a partition with less than n items."
+ {:added "1.12"}
+ ([n coll]
+ (partitionv n n coll))
+ ([n step coll]
+ (lazy-seq
+ (when-let [s (seq coll)]
+ (let [p (into [] (take n) s)]
+ (when (= n (count p))
+ (cons p (partitionv n step (nthrest s step))))))))
+ ([n step pad coll]
+ (lazy-seq
+ (when-let [s (seq coll)]
+ (let [p (into [] (take n) s)]
+ (if (= n (count p))
+ (cons p (partitionv n step pad (nthrest s step)))
+ (into [] (take n) (concat p pad))))))))
+
+(defn partitionv-all
+ "Returns a lazy sequence of vector partitions, but may include
+ partitions with fewer than n items at the end.
+ Returns a stateful transducer when no collection is provided."
+ {:added "1.12"}
+ ([n]
+ (partition-all n))
+ ([n coll]
+ (partitionv-all n n coll))
+ ([n step coll]
+ (lazy-seq
+ (when-let [s (seq coll)]
+ (let [seg (into [] (take n) coll)]
+ (cons seg (partitionv-all n step (drop step s))))))))
+
(defn shuffle
"Return a random permutation of coll"
{:added "1.2"
diff --git a/src/jvm/clojure/lang/IDrop.java b/src/jvm/clojure/lang/IDrop.java
new file mode 100644
index 0000000000..ce4341ce02
--- /dev/null
+++ b/src/jvm/clojure/lang/IDrop.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) Rich Hickey. All rights reserved.
+ * The use and distribution terms for this software are covered by the
+ * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+ * which can be found in the file epl-v10.html at the root of this distribution.
+ * By using this software in any fashion, you are agreeing to be bound by
+ * the terms of this license.
+ * You must not remove this notice, or any other, from this software.
+ **/
+
+package clojure.lang;
+
+/**
+ * Persistent or algorithmically defined collections can implement IDrop to provide
+ * a means of dropping N items that is more efficient than sequential walking.
+ */
+public interface IDrop{
+ /**
+ * Returns a collection that is Sequential, ISeq, and IReduceInit. It is also
+ * useful if the returned coll implements IDrop for subsequent use in a
+ * partition-like scenario.
+ *
+ * If n is <= 0, return this.
+ * If n drops to or past the end of the collection, return null.
+ *
+ * @param n Items to drop
+ * @return Collection that is Sequential, ISeq, and IReduceInit
+ */
+ Sequential drop(int n);
+}
diff --git a/src/jvm/clojure/lang/LongRange.java b/src/jvm/clojure/lang/LongRange.java
index 348e362081..d1bfba2fe0 100644
--- a/src/jvm/clojure/lang/LongRange.java
+++ b/src/jvm/clojure/lang/LongRange.java
@@ -16,87 +16,82 @@
import java.util.NoSuchElementException;
/**
- * Implements the special common case of a finite range based on long start, end, and step.
+ * Implements the special common case of a finite range based on long start, end, and step,
+ * with no more than Integer.MAX_VALUE items.
*/
-public class LongRange extends ASeq implements Counted, IChunkedSeq, IReduce {
+public class LongRange extends ASeq implements Counted, IChunkedSeq, IReduce, IDrop {
-private static final int CHUNK_SIZE = 32;
+private static final long serialVersionUID = -1467242400566893909L;
// Invariants guarantee this is never an empty or infinite seq
// assert(start != end && step != 0)
final long start;
final long end;
final long step;
-final BoundsCheck boundsCheck;
-private volatile LongChunk _chunk; // lazy
-private volatile ISeq _chunkNext; // lazy
-private volatile ISeq _next; // cached
+final int count;
-private static interface BoundsCheck extends Serializable {
- boolean exceededBounds(long val);
-}
-
-private static BoundsCheck positiveStep(final long end) {
- return new BoundsCheck() {
- public boolean exceededBounds(long val){
- return (val >= end);
- }
- };
-}
-
-private static BoundsCheck negativeStep(final long end) {
- return new BoundsCheck() {
- public boolean exceededBounds(long val){
- return (val <= end);
- }
- };
-}
-
-private LongRange(long start, long end, long step, BoundsCheck boundsCheck){
+private LongRange(long start, long end, long step, int count){
this.start = start;
this.end = end;
this.step = step;
- this.boundsCheck = boundsCheck;
+ this.count = count;
}
-private LongRange(long start, long end, long step, BoundsCheck boundsCheck, LongChunk chunk, ISeq chunkNext){
+private LongRange(IPersistentMap meta, long start, long end, long step, int count){
+ super(meta);
this.start = start;
this.end = end;
this.step = step;
- this.boundsCheck = boundsCheck;
- this._chunk = chunk;
- this._chunkNext = chunkNext;
+ this.count = count;
}
-private LongRange(IPersistentMap meta, long start, long end, long step, BoundsCheck boundsCheck, LongChunk chunk, ISeq chunkNext){
- super(meta);
- this.start = start;
- this.end = end;
- this.step = step;
- this.boundsCheck = boundsCheck;
- this._chunk = chunk;
- this._chunkNext = chunkNext;
+// returns exact size of remaining items OR throws ArithmeticException for overflow case
+static long rangeCount(long start, long end, long step) {
+ // (1) count = ceiling ( (end - start) / step )
+ // (2) ceiling(a/b) = (a+b+o)/b where o=-1 for positive stepping and +1 for negative stepping
+ // thus: count = end - start + step + o / step
+ return Numbers.add(Numbers.add(Numbers.minus(end, start), step), step > 0 ? -1 : 1) / step;
}
public static ISeq create(long end) {
- if(end > 0)
- return new LongRange(0L, end, 1L, positiveStep(end));
- return PersistentList.EMPTY;
+ if(end > 0) {
+ try {
+ return new LongRange(0L, end, 1L, Math.toIntExact(rangeCount(0L, end, 1L)));
+ } catch(ArithmeticException e) {
+ return Range.create(end); // count > Integer.MAX_VALUE
+ }
+ } else {
+ return PersistentList.EMPTY;
+ }
}
public static ISeq create(long start, long end) {
- if(start >= end)
+ if(start >= end) {
return PersistentList.EMPTY;
- return new LongRange(start, end, 1L, positiveStep(end));
+ } else {
+ try {
+ return new LongRange(start, end, 1L, Math.toIntExact(rangeCount(start, end, 1L)));
+ } catch(ArithmeticException e) {
+ return Range.create(start, end);
+ }
+ }
}
public static ISeq create(final long start, long end, long step) {
if(step > 0) {
if(end <= start) return PersistentList.EMPTY;
- return new LongRange(start, end, step, positiveStep(end));
+ try {
+ return new LongRange(start, end, step, Math.toIntExact(rangeCount(start, end, step)));
+ } catch(ArithmeticException e) {
+ return Range.create(start, end, step);
+ }
} else if(step < 0) {
if(end >= start) return PersistentList.EMPTY;
- return new LongRange(start, end, step, negativeStep(end));
+ try {
+ return new LongRange(start, end, step, Math.toIntExact(rangeCount(start, end, step)));
+ } catch(ArithmeticException e) {
+ return Range.create(start, end, step);
+ }
} else {
if(end == start) return PersistentList.EMPTY;
return Repeat.create(start);
@@ -106,134 +101,70 @@ public static ISeq create(final long start, long end, long step) {
public Obj withMeta(IPersistentMap meta){
if(meta == _meta)
return this;
- return new LongRange(meta, start, end, step, boundsCheck, _chunk, _chunkNext);
+ return new LongRange(meta, start, end, step, count);
}
public Object first() {
return start;
}
-public void forceChunk() {
- if(_chunk != null) return;
-
- long count;
- try {
- count = rangeCount(start, end, step);
- } catch(ArithmeticException e) {
- // size of total range is > Long.MAX_VALUE so must step to count
- // this only happens in pathological range cases like:
- // (range -9223372036854775808 9223372036854775807 9223372036854775807)
- count = steppingCount(start, end, step);
- }
-
- if (count > CHUNK_SIZE) { // not last chunk
- long nextStart = start + (step * CHUNK_SIZE); // cannot overflow, must be < end
- _chunkNext = new LongRange(nextStart, end, step, boundsCheck);
- _chunk = new LongChunk(start, step, CHUNK_SIZE);
- } else { // last chunk
- _chunk = new LongChunk(start, step, (int) count); // count must be <= CHUNK_SIZE
- }
-}
-
public ISeq next() {
- if(_next != null)
- return _next;
-
- forceChunk();
- if(_chunk.count() > 1) {
- LongChunk smallerChunk = _chunk.dropFirst();
- _next = new LongRange(smallerChunk.first(), end, step, boundsCheck, smallerChunk, _chunkNext);
- return _next;
+ if(count > 1) {
+ return new LongRange(start + step, end, step, count-1);
+ } else {
+ return null;
}
- return chunkedNext();
}
public IChunk chunkedFirst() {
- forceChunk();
- return _chunk;
+ return new LongChunk(start, step, count);
}
public ISeq chunkedNext() {
- return chunkedMore().seq();
+ return null;
}
public ISeq chunkedMore() {
- forceChunk();
- if(_chunkNext == null)
- return PersistentList.EMPTY;
- return _chunkNext;
+ return PersistentList.EMPTY;
}
-// fallback count mechanism for pathological cases
-// returns either exact count or CHUNK_SIZE+1
-long steppingCount(long start, long end, long step) {
- long count = 1;
- long s = start;
- while(count <= CHUNK_SIZE) {
- try {
- s = Numbers.add(s, step);
- if(boundsCheck.exceededBounds(s))
- break;
- else
- count++;
- } catch(ArithmeticException e) {
- break;
- }
+public Sequential drop(int n) {
+ if(n <= 0) {
+ return this;
+ } else if(n < count) {
+ return new LongRange(start+(step*n), end, step, count - n);
+ } else {
+ return null;
}
- return count;
-}
-
-// returns exact size of remaining items OR throws ArithmeticException for overflow case
-long rangeCount(long start, long end, long step) {
- // (1) count = ceiling ( (end - start) / step )
- // (2) ceiling(a/b) = (a+b+o)/b where o=-1 for positive stepping and +1 for negative stepping
- // thus: count = end - start + step + o / step
- return Numbers.add(Numbers.add(Numbers.minus(end, start), step), this.step > 0 ? -1 : 1) / step;
}
public int count() {
- try {
- long c = rangeCount(start, end, step);
- if(c > Integer.MAX_VALUE) {
- return Numbers.throwIntOverflow();
- } else {
- return (int) c;
- }
- } catch(ArithmeticException e) {
- // rare case from large range or step, fall back to iterating and counting
- Iterator iter = this.iterator();
- long count = 0;
- while(iter.hasNext()) {
- iter.next();
- count++;
- }
-
- if(count > Integer.MAX_VALUE)
- return Numbers.throwIntOverflow();
- else
- return (int)count;
- }
+ return count;
}
public Object reduce(IFn f) {
Object acc = start;
long i = start + step;
- while(! boundsCheck.exceededBounds(i)) {
+ int n = count;
+ while(n > 1) {
acc = f.invoke(acc, i);
if (acc instanceof Reduced) return ((Reduced)acc).deref();
i += step;
+ n--;
}
return acc;
}
public Object reduce(IFn f, Object val) {
Object acc = val;
+ int n = count;
long i = start;
do {
acc = f.invoke(acc, i);
if (RT.isReduced(acc)) return ((Reduced)acc).deref();
i += step;
- } while(! boundsCheck.exceededBounds(i));
+ n--;
+ } while(n > 0);
return acc;
}
@@ -243,26 +174,22 @@ public Iterator iterator() {
class LongRangeIterator implements Iterator {
private long next;
- private boolean hasNext;
+ private int remaining;
public LongRangeIterator() {
this.next = start;
- this.hasNext = true;
+ this.remaining = count;
}
public boolean hasNext() {
- return hasNext;
+ return remaining > 0;
}
public Object next() {
- if (hasNext) {
+ if (remaining > 0) {
long ret = next;
- try {
- next = Numbers.add(next, step);
- hasNext = ! boundsCheck.exceededBounds(next);
- } catch(ArithmeticException e) {
- hasNext = false;
- }
+ next = next + step;
+ remaining = remaining - 1;
return ret;
} else {
throw new NoSuchElementException();
@@ -322,4 +249,4 @@ public Object reduce(IFn f, Object init) {
}
}
-}
\ No newline at end of file
+}
diff --git a/src/jvm/clojure/lang/PersistentArrayMap.java b/src/jvm/clojure/lang/PersistentArrayMap.java
index 1f86fd80aa..73e8c023cb 100644
--- a/src/jvm/clojure/lang/PersistentArrayMap.java
+++ b/src/jvm/clojure/lang/PersistentArrayMap.java
@@ -27,7 +27,7 @@
* null keys and values are ok, but you won't be able to distinguish a null value via valAt - use contains/entryAt
*/
-public class PersistentArrayMap extends APersistentMap implements IObj, IEditableCollection, IMapIterable, IKVReduce{
+public class PersistentArrayMap extends APersistentMap implements IObj, IEditableCollection, IMapIterable, IKVReduce, IDrop{
final Object[] array;
static final int HASHTABLE_THRESHOLD = 16;
@@ -348,11 +348,19 @@ public ISeq seq(){
return null;
}
+public Sequential drop(int n) {
+ if(array.length > 0) {
+ return ((Seq) seq()).drop(n);
+ } else {
+ return null;
+ }
+}
+
public IPersistentMap meta(){
return _meta;
}
-static class Seq extends ASeq implements Counted{
+static class Seq extends ASeq implements Counted, IReduce, IDrop {
final Object[] array;
final int i;
@@ -381,16 +389,52 @@ public int count(){
return (array.length - i) / 2;
}
+ public Sequential drop(int n) {
+ if(n < count()) {
+ return new Seq(array, i + (2 * n));
+ } else {
+ return null;
+ }
+ }
+
public Obj withMeta(IPersistentMap meta){
if(meta() == meta)
return this;
return new Seq(meta, array, i);
}
+
+ public Iterator iterator() {
+ return new Iter(array, i-2, APersistentMap.MAKE_ENTRY);
+ }
+
+ public Object reduce(IFn f) {
+ if(i < array.length) {
+ Object acc = MapEntry.create(array[i], array[i+1]);
+ for(int j=i+2;j < array.length;j+=2){
+ acc = f.invoke(acc, MapEntry.create(array[j], array[j+1]));
+ if(RT.isReduced(acc))
+ return ((IDeref)acc).deref();
+ }
+ return acc;
+ } else {
+ return f.invoke();
+ }
+ }
+
+ public Object reduce(IFn f, Object init) {
+ Object acc = init;
+ for(int j=i;j < array.length;j+=2){
+ acc = f.invoke(acc, MapEntry.create(array[j], array[j+1]));
+ if(RT.isReduced(acc))
+ return ((IDeref)acc).deref();
+ }
+ return acc;
+ }
}
static class Iter implements Iterator{
- IFn f;
- Object[] array;
+ final IFn f;
+ final Object[] array;
int i;
//for iterator
diff --git a/src/jvm/clojure/lang/PersistentVector.java b/src/jvm/clojure/lang/PersistentVector.java
index fcd9e46c26..9f02353965 100644
--- a/src/jvm/clojure/lang/PersistentVector.java
+++ b/src/jvm/clojure/lang/PersistentVector.java
@@ -19,7 +19,7 @@
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicReference;
-public class PersistentVector extends APersistentVector implements IObj, IEditableCollection, IReduce, IKVReduce{
+public class PersistentVector extends APersistentVector implements IObj, IEditableCollection, IReduce, IKVReduce, IDrop{
public static class Node implements Serializable {
transient public final AtomicReference edit;
@@ -363,7 +363,18 @@ public Object kvreduce(IFn f, Object init){
return init;
}
-static public final class ChunkedSeq extends ASeq implements IChunkedSeq,Counted{
+public Sequential drop(int n) {
+ if(n < 0) {
+ return this;
+ } else if(n < cnt) {
+ int offset = n%32;
+ return new ChunkedSeq(this, this.arrayFor(n), n-offset, offset);
+ } else {
+ return null;
+ }
+}
+
+static public final class ChunkedSeq extends ASeq implements IChunkedSeq,Counted,IReduce,IDrop{
public final PersistentVector vec;
final Object[] node;
@@ -428,6 +439,73 @@ public ISeq next(){
public int count(){
return vec.cnt - (i + offset);
}
+
+ public Iterator iterator() {
+ return vec.rangedIterator(i +offset, vec.cnt);
+ }
+
+ public Object reduce(IFn f) {
+ Object acc;
+ if (i +offset < vec.cnt)
+ acc = node[offset];
+ else
+ return f.invoke();
+
+ for(int j=offset+1;j 1) {
+ return new Repeat(count-n, val);
+ } else if(count == INFINITE) {
+ return this;
+ } else {
+ return null;
+ }
+}
+
}
diff --git a/src/jvm/clojure/lang/StringSeq.java b/src/jvm/clojure/lang/StringSeq.java
index bcb269d5dc..cc93dea55b 100644
--- a/src/jvm/clojure/lang/StringSeq.java
+++ b/src/jvm/clojure/lang/StringSeq.java
@@ -12,7 +12,10 @@
package clojure.lang;
-public class StringSeq extends ASeq implements IndexedSeq{
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public class StringSeq extends ASeq implements IndexedSeq,IDrop,IReduceInit{
public final CharSequence s;
public final int i;
@@ -51,4 +54,24 @@ public int index(){
public int count(){
return s.length() - i;
}
+
+public Sequential drop(int n) {
+ int ii = i + n;
+ if (ii < s.length()) {
+ return new StringSeq(_meta, s, ii);
+ } else {
+ return null;
+ }
+}
+
+public Object reduce(IFn f, Object start) {
+ Object acc = start;
+ for(int ii=i; ii < s.length(); ii++) {
+ acc = f.invoke(acc, s.charAt(ii));
+ if(RT.isReduced(acc))
+ return ((IDeref)acc).deref();
+ }
+ return acc;
+}
+
}
diff --git a/test/clojure/test_clojure/sequences.clj b/test/clojure/test_clojure/sequences.clj
index 4eb99e7860..c684a6b298 100644
--- a/test/clojure/test_clojure/sequences.clj
+++ b/test/clojure/test_clojure/sequences.clj
@@ -796,6 +796,23 @@
(is (= (assoc (array-map (repeat 1 :x) :y) '(:x) :z) {'(:x) :z}))
(is (= (assoc (hash-map (repeat 1 :x) :y) '(:x) :z) {'(:x) :z})))
+(deftest test-partitionv
+ (are [x y] (= x y)
+ (partitionv 2 [1 2 3]) '((1 2))
+ (partitionv 2 [1 2 3 4]) '((1 2) (3 4))
+ (partitionv 2 []) ()
+
+ (partitionv 2 3 [1 2 3 4 5 6 7]) '((1 2) (4 5))
+ (partitionv 2 3 [1 2 3 4 5 6 7 8]) '((1 2) (4 5) (7 8))
+ (partitionv 2 3 []) ()
+
+ (partitionv 1 []) ()
+ (partitionv 1 [1 2 3]) '((1) (2) (3))
+
+ (partitionv 5 [1 2 3]) ()
+
+ (partitionv -1 [1 2 3]) ()
+ (partitionv -2 [1 2 3]) () ))
(deftest test-iterate
(are [x y] (= x y)
@@ -1331,6 +1348,12 @@
(is (= (partition-all 4 2 [1 2 3 4 5 6 7 8 9])
[[1 2 3 4] [3 4 5 6] [5 6 7 8] [7 8 9] [9]])))
+(deftest test-partitionv-all
+ (is (= (partitionv-all 4 [1 2 3 4 5 6 7 8 9])
+ [[1 2 3 4] [5 6 7 8] [9]]))
+ (is (= (partitionv-all 4 2 [1 2 3 4 5 6 7 8 9])
+ [[1 2 3 4] [3 4 5 6] [5 6 7 8] [7 8 9] [9]])))
+
(deftest test-shuffle-invariants
(is (= (count (shuffle [1 2 3 4])) 4))
(let [shuffled-seq (shuffle [1 2 3 4])]
@@ -1458,6 +1481,42 @@
:kf #(some-> % :k #{0 1 2})
:vf :item))))))
+(deftest test-reduce-on-coll-seqs
+ ;; reduce on seq of coll, both with and without an init
+ (are [coll expected expected-init]
+ (and
+ (= expected-init (reduce conj [:init] (seq coll)))
+ (= expected (reduce conj (seq coll))))
+ ;; (seq [ ... ])
+ [] [] [:init]
+ [1] 1 [:init 1]
+ [[1] 2] [1 2] [:init [1] 2]
+
+ ;; (seq { ... })
+ {} [] [:init]
+ {1 1} [1 1] [:init [1 1]]
+ {1 1 2 2} [1 1 [2 2]] [:init [1 1] [2 2]]
+
+ ;; (seq (hash-map ... ))
+ (hash-map) [] [:init]
+ (hash-map 1 1) [1 1] [:init [1 1]]
+ (hash-map 1 1 2 2) [1 1 [2 2]] [:init [1 1] [2 2]]
+
+ ;; (seq (sorted-map ... ))
+ (sorted-map) [] [:init]
+ (sorted-map 1 1) [1 1] [:init [1 1]]
+ (sorted-map 1 1 2 2) [1 1 [2 2]] [:init [1 1] [2 2]])
+
+ (are [coll expected expected-init]
+ (and
+ (= expected-init (reduce + 100 (seq coll)))
+ (= expected (reduce + (seq coll))))
+
+ ;; (seq (range ...))
+ (range 0) 0 100
+ (range 1 2) 1 101
+ (range 1 3) 3 103))
+
(defspec iteration-seq-equals-reduce 1000
(prop/for-all [initk gen/int
seed gen/int]
From 56d37996b18df811c20f391c840e7fd26ed2f58d Mon Sep 17 00:00:00 2001
From: Fogus
Date: Thu, 9 Jun 2022 09:27:33 -0500
Subject: [PATCH 118/285] CLJ-1327: Pinned the serialVersionUID for Clojure
types to v1.10.3 values
---
src/jvm/clojure/lang/AFunction.java | 2 ++
src/jvm/clojure/lang/AMapEntry.java | 2 ++
src/jvm/clojure/lang/APersistentMap.java | 3 +++
src/jvm/clojure/lang/APersistentSet.java | 3 +++
src/jvm/clojure/lang/APersistentVector.java | 3 +++
src/jvm/clojure/lang/ASeq.java | 3 +++
src/jvm/clojure/lang/ArityException.java | 2 ++
src/jvm/clojure/lang/ArrayChunk.java | 2 ++
src/jvm/clojure/lang/BigInt.java | 2 ++
src/jvm/clojure/lang/ChunkedCons.java | 2 ++
src/jvm/clojure/lang/Cons.java | 2 ++
src/jvm/clojure/lang/Cycle.java | 2 ++
src/jvm/clojure/lang/EnumerationSeq.java | 3 +++
src/jvm/clojure/lang/ExceptionInfo.java | 3 +++
src/jvm/clojure/lang/FnLoaderThunk.java | 2 ++
src/jvm/clojure/lang/Iterate.java | 2 ++
src/jvm/clojure/lang/IteratorSeq.java | 3 +++
src/jvm/clojure/lang/LazySeq.java | 2 ++
src/jvm/clojure/lang/MapEntry.java | 3 +++
src/jvm/clojure/lang/Namespace.java | 3 +++
src/jvm/clojure/lang/Obj.java | 2 ++
src/jvm/clojure/lang/PersistentArrayMap.java | 2 ++
src/jvm/clojure/lang/PersistentHashMap.java | 2 ++
src/jvm/clojure/lang/PersistentHashSet.java | 2 ++
src/jvm/clojure/lang/PersistentList.java | 2 ++
src/jvm/clojure/lang/PersistentQueue.java | 2 ++
src/jvm/clojure/lang/PersistentStructMap.java | 2 ++
src/jvm/clojure/lang/PersistentVector.java | 2 ++
src/jvm/clojure/lang/Range.java | 2 ++
src/jvm/clojure/lang/Ratio.java | 3 +++
src/jvm/clojure/lang/Repeat.java | 2 ++
src/jvm/clojure/lang/RestFn.java | 3 +++
src/jvm/clojure/lang/StringSeq.java | 3 +++
src/jvm/clojure/lang/Symbol.java | 3 +++
src/jvm/clojure/lang/Var.java | 2 ++
35 files changed, 83 insertions(+)
diff --git a/src/jvm/clojure/lang/AFunction.java b/src/jvm/clojure/lang/AFunction.java
index 2ef5cd9b3a..4ab8c6932b 100644
--- a/src/jvm/clojure/lang/AFunction.java
+++ b/src/jvm/clojure/lang/AFunction.java
@@ -17,6 +17,8 @@
public abstract class AFunction extends AFn implements IObj, Comparator, Fn, Serializable {
+private static final long serialVersionUID = 4469383498184457675L;
+
public volatile MethodImplCache __methodImplCache;
public IPersistentMap meta(){
diff --git a/src/jvm/clojure/lang/AMapEntry.java b/src/jvm/clojure/lang/AMapEntry.java
index 41ae7563a1..56debb4f1c 100644
--- a/src/jvm/clojure/lang/AMapEntry.java
+++ b/src/jvm/clojure/lang/AMapEntry.java
@@ -16,6 +16,8 @@
public abstract class AMapEntry extends APersistentVector implements IMapEntry{
+private static final long serialVersionUID = -5007980429903443802L;
+
public Object nth(int i){
if(i == 0)
return key();
diff --git a/src/jvm/clojure/lang/APersistentMap.java b/src/jvm/clojure/lang/APersistentMap.java
index 7f96271278..3dfb73a5ae 100644
--- a/src/jvm/clojure/lang/APersistentMap.java
+++ b/src/jvm/clojure/lang/APersistentMap.java
@@ -14,6 +14,9 @@
import java.util.*;
public abstract class APersistentMap extends AFn implements IPersistentMap, Map, Iterable, Serializable, MapEquivalence, IHashEq {
+
+private static final long serialVersionUID = 6736310834519110267L;
+
int _hash;
int _hasheq;
diff --git a/src/jvm/clojure/lang/APersistentSet.java b/src/jvm/clojure/lang/APersistentSet.java
index 1c2ce8f46c..2c8caad85b 100644
--- a/src/jvm/clojure/lang/APersistentSet.java
+++ b/src/jvm/clojure/lang/APersistentSet.java
@@ -18,6 +18,9 @@
import java.util.Set;
public abstract class APersistentSet extends AFn implements IPersistentSet, Collection, Set, Serializable, IHashEq {
+
+private static final long serialVersionUID = 889908853183699706L;
+
int _hash;
int _hasheq;
final IPersistentMap impl;
diff --git a/src/jvm/clojure/lang/APersistentVector.java b/src/jvm/clojure/lang/APersistentVector.java
index 3e88d14dbf..07115684aa 100644
--- a/src/jvm/clojure/lang/APersistentVector.java
+++ b/src/jvm/clojure/lang/APersistentVector.java
@@ -19,6 +19,9 @@ public abstract class APersistentVector extends AFn implements IPersistentVector
List,
RandomAccess, Comparable,
Serializable, IHashEq {
+
+private static final long serialVersionUID = 4667575149454420891L;
+
int _hash;
int _hasheq;
diff --git a/src/jvm/clojure/lang/ASeq.java b/src/jvm/clojure/lang/ASeq.java
index f0f8e5d447..ec1b4d67f4 100644
--- a/src/jvm/clojure/lang/ASeq.java
+++ b/src/jvm/clojure/lang/ASeq.java
@@ -14,6 +14,9 @@
import java.util.*;
public abstract class ASeq extends Obj implements ISeq, Sequential, List, Serializable, IHashEq {
+
+private static final long serialVersionUID = 4748650717905139299L;
+
transient int _hash;
transient int _hasheq;
diff --git a/src/jvm/clojure/lang/ArityException.java b/src/jvm/clojure/lang/ArityException.java
index 49b7914b70..5c0f384f1a 100644
--- a/src/jvm/clojure/lang/ArityException.java
+++ b/src/jvm/clojure/lang/ArityException.java
@@ -15,6 +15,8 @@
*/
public class ArityException extends IllegalArgumentException {
+ private static final long serialVersionUID = 2265783180488869950L;
+
final public int actual;
final public String name;
diff --git a/src/jvm/clojure/lang/ArrayChunk.java b/src/jvm/clojure/lang/ArrayChunk.java
index 97430839b9..ba6334cf54 100644
--- a/src/jvm/clojure/lang/ArrayChunk.java
+++ b/src/jvm/clojure/lang/ArrayChunk.java
@@ -16,6 +16,8 @@
public final class ArrayChunk implements IChunk, Serializable {
+private static final long serialVersionUID = -8302142882294545702L;
+
final Object[] array;
final int off;
final int end;
diff --git a/src/jvm/clojure/lang/BigInt.java b/src/jvm/clojure/lang/BigInt.java
index a3ca969410..1f2b7580d5 100644
--- a/src/jvm/clojure/lang/BigInt.java
+++ b/src/jvm/clojure/lang/BigInt.java
@@ -17,6 +17,8 @@
public final class BigInt extends Number implements IHashEq{
+private static final long serialVersionUID = 5097771279236135022L;
+
final public long lpart;
final public BigInteger bipart;
diff --git a/src/jvm/clojure/lang/ChunkedCons.java b/src/jvm/clojure/lang/ChunkedCons.java
index b52bc2b62a..25b32200d3 100644
--- a/src/jvm/clojure/lang/ChunkedCons.java
+++ b/src/jvm/clojure/lang/ChunkedCons.java
@@ -14,6 +14,8 @@
final public class ChunkedCons extends ASeq implements IChunkedSeq{
+private static final long serialVersionUID = 2773920188566401743L;
+
final IChunk chunk;
final ISeq _more;
diff --git a/src/jvm/clojure/lang/Cons.java b/src/jvm/clojure/lang/Cons.java
index 88dceef395..9378881ddc 100644
--- a/src/jvm/clojure/lang/Cons.java
+++ b/src/jvm/clojure/lang/Cons.java
@@ -16,6 +16,8 @@
final public class Cons extends ASeq implements Serializable {
+private static final long serialVersionUID = 6682587018567831263L;
+
private final Object _first;
private final ISeq _more;
diff --git a/src/jvm/clojure/lang/Cycle.java b/src/jvm/clojure/lang/Cycle.java
index 877695558a..a605e8ca20 100644
--- a/src/jvm/clojure/lang/Cycle.java
+++ b/src/jvm/clojure/lang/Cycle.java
@@ -14,6 +14,8 @@
public class Cycle extends ASeq implements IReduce, IPending {
+private static final long serialVersionUID = 4007270937279943908L;
+
private final ISeq all; // never null
private final ISeq prev;
private volatile ISeq _current; // lazily realized
diff --git a/src/jvm/clojure/lang/EnumerationSeq.java b/src/jvm/clojure/lang/EnumerationSeq.java
index 3616fa787c..acd13ca1e8 100644
--- a/src/jvm/clojure/lang/EnumerationSeq.java
+++ b/src/jvm/clojure/lang/EnumerationSeq.java
@@ -17,6 +17,9 @@
import java.util.Enumeration;
public class EnumerationSeq extends ASeq{
+
+private static final long serialVersionUID = 5227192199685595994L;
+
final Enumeration iter;
final State state;
diff --git a/src/jvm/clojure/lang/ExceptionInfo.java b/src/jvm/clojure/lang/ExceptionInfo.java
index 92040d21e0..f7b704deab 100644
--- a/src/jvm/clojure/lang/ExceptionInfo.java
+++ b/src/jvm/clojure/lang/ExceptionInfo.java
@@ -16,6 +16,9 @@
* exception classes.
*/
public class ExceptionInfo extends RuntimeException implements IExceptionInfo {
+
+ private static final long serialVersionUID = -1073473305916521986L;
+
public final IPersistentMap data;
public ExceptionInfo(String s, IPersistentMap data) {
diff --git a/src/jvm/clojure/lang/FnLoaderThunk.java b/src/jvm/clojure/lang/FnLoaderThunk.java
index 692e45c752..1411bf4902 100644
--- a/src/jvm/clojure/lang/FnLoaderThunk.java
+++ b/src/jvm/clojure/lang/FnLoaderThunk.java
@@ -14,6 +14,8 @@
public class FnLoaderThunk extends RestFn{
+private static final long serialVersionUID = 2194257205455463687L;
+
final Var v;
final ClassLoader loader;
final String fnClassName;
diff --git a/src/jvm/clojure/lang/Iterate.java b/src/jvm/clojure/lang/Iterate.java
index aec0c14aa2..0bbe733d94 100644
--- a/src/jvm/clojure/lang/Iterate.java
+++ b/src/jvm/clojure/lang/Iterate.java
@@ -14,6 +14,8 @@
public class Iterate extends ASeq implements IReduce, IPending {
+private static final long serialVersionUID = -78221705247226450L;
+
private static final Object UNREALIZED_SEED = new Object();
private final IFn f; // never null
private final Object prevSeed;
diff --git a/src/jvm/clojure/lang/IteratorSeq.java b/src/jvm/clojure/lang/IteratorSeq.java
index 9ecdbc48df..18b9cf3503 100644
--- a/src/jvm/clojure/lang/IteratorSeq.java
+++ b/src/jvm/clojure/lang/IteratorSeq.java
@@ -15,6 +15,9 @@
import java.util.Iterator;
public class IteratorSeq extends ASeq{
+
+private static final long serialVersionUID = -2631916503522522760L;
+
final Iterator iter;
final State state;
diff --git a/src/jvm/clojure/lang/LazySeq.java b/src/jvm/clojure/lang/LazySeq.java
index 0ca4e46223..f9f9bfb24e 100644
--- a/src/jvm/clojure/lang/LazySeq.java
+++ b/src/jvm/clojure/lang/LazySeq.java
@@ -16,6 +16,8 @@
public final class LazySeq extends Obj implements ISeq, Sequential, List, IPending, IHashEq{
+private static final long serialVersionUID = 7700080124382322592L;
+
private IFn fn;
private Object sv;
private ISeq s;
diff --git a/src/jvm/clojure/lang/MapEntry.java b/src/jvm/clojure/lang/MapEntry.java
index 310199c1db..777b262734 100644
--- a/src/jvm/clojure/lang/MapEntry.java
+++ b/src/jvm/clojure/lang/MapEntry.java
@@ -13,6 +13,9 @@
import java.util.Iterator;
public class MapEntry extends AMapEntry{
+
+private static final long serialVersionUID = -3752414622414469244L;
+
final Object _key;
final Object _val;
diff --git a/src/jvm/clojure/lang/Namespace.java b/src/jvm/clojure/lang/Namespace.java
index 68cded632e..7fd465fc42 100644
--- a/src/jvm/clojure/lang/Namespace.java
+++ b/src/jvm/clojure/lang/Namespace.java
@@ -18,6 +18,9 @@
import java.util.concurrent.atomic.AtomicReference;
public class Namespace extends AReference implements Serializable {
+
+private static final long serialVersionUID = -3134444395801383865L;
+
final public Symbol name;
transient final AtomicReference mappings = new AtomicReference();
transient final AtomicReference aliases = new AtomicReference();
diff --git a/src/jvm/clojure/lang/Obj.java b/src/jvm/clojure/lang/Obj.java
index bd7ee91878..655d09117d 100644
--- a/src/jvm/clojure/lang/Obj.java
+++ b/src/jvm/clojure/lang/Obj.java
@@ -16,6 +16,8 @@
public abstract class Obj implements IObj, Serializable {
+private static final long serialVersionUID = 802029099426284526L;
+
final IPersistentMap _meta;
public Obj(IPersistentMap meta){
diff --git a/src/jvm/clojure/lang/PersistentArrayMap.java b/src/jvm/clojure/lang/PersistentArrayMap.java
index 73e8c023cb..18bbcaa7b3 100644
--- a/src/jvm/clojure/lang/PersistentArrayMap.java
+++ b/src/jvm/clojure/lang/PersistentArrayMap.java
@@ -29,6 +29,8 @@
public class PersistentArrayMap extends APersistentMap implements IObj, IEditableCollection, IMapIterable, IKVReduce, IDrop{
+private static final long serialVersionUID = -2074065891090893601L;
+
final Object[] array;
static final int HASHTABLE_THRESHOLD = 16;
diff --git a/src/jvm/clojure/lang/PersistentHashMap.java b/src/jvm/clojure/lang/PersistentHashMap.java
index 0cb1259796..8f15944bc6 100644
--- a/src/jvm/clojure/lang/PersistentHashMap.java
+++ b/src/jvm/clojure/lang/PersistentHashMap.java
@@ -27,6 +27,8 @@
public class PersistentHashMap extends APersistentMap implements IEditableCollection, IObj, IMapIterable, IKVReduce {
+private static final long serialVersionUID = -8682496769319143320L;
+
final int count;
final INode root;
final boolean hasNull;
diff --git a/src/jvm/clojure/lang/PersistentHashSet.java b/src/jvm/clojure/lang/PersistentHashSet.java
index d3a7162605..b481a9443c 100644
--- a/src/jvm/clojure/lang/PersistentHashSet.java
+++ b/src/jvm/clojure/lang/PersistentHashSet.java
@@ -16,6 +16,8 @@
public class PersistentHashSet extends APersistentSet implements IObj, IEditableCollection {
+private static final long serialVersionUID = 6973890746204954938L;
+
static public final PersistentHashSet EMPTY = new PersistentHashSet(null, PersistentHashMap.EMPTY);
final IPersistentMap _meta;
diff --git a/src/jvm/clojure/lang/PersistentList.java b/src/jvm/clojure/lang/PersistentList.java
index 618d52fca0..225651d663 100644
--- a/src/jvm/clojure/lang/PersistentList.java
+++ b/src/jvm/clojure/lang/PersistentList.java
@@ -15,6 +15,8 @@
public class PersistentList extends ASeq implements IPersistentList, IReduce, List, Counted {
+private static final long serialVersionUID = -8833289659955219995L;
+
private final Object _first;
private final IPersistentList _rest;
private final int _count;
diff --git a/src/jvm/clojure/lang/PersistentQueue.java b/src/jvm/clojure/lang/PersistentQueue.java
index af916193e9..881c190028 100644
--- a/src/jvm/clojure/lang/PersistentQueue.java
+++ b/src/jvm/clojure/lang/PersistentQueue.java
@@ -24,6 +24,8 @@
public class PersistentQueue extends Obj implements IPersistentList, Collection, Counted, IHashEq{
+private static final long serialVersionUID = 8247184423915313132L;
+
final public static PersistentQueue EMPTY = new PersistentQueue(null, 0, null, null);
//*
diff --git a/src/jvm/clojure/lang/PersistentStructMap.java b/src/jvm/clojure/lang/PersistentStructMap.java
index 734428c06c..7e9c92e317 100644
--- a/src/jvm/clojure/lang/PersistentStructMap.java
+++ b/src/jvm/clojure/lang/PersistentStructMap.java
@@ -19,6 +19,8 @@
public class PersistentStructMap extends APersistentMap implements IObj{
+private static final long serialVersionUID = -2701411408470234065L;
+
public static class Def implements Serializable{
final ISeq keys;
final IPersistentMap keyslots;
diff --git a/src/jvm/clojure/lang/PersistentVector.java b/src/jvm/clojure/lang/PersistentVector.java
index 9f02353965..4b9f3260ad 100644
--- a/src/jvm/clojure/lang/PersistentVector.java
+++ b/src/jvm/clojure/lang/PersistentVector.java
@@ -21,6 +21,8 @@
public class PersistentVector extends APersistentVector implements IObj, IEditableCollection, IReduce, IKVReduce, IDrop{
+private static final long serialVersionUID = -7896022351281214157L;
+
public static class Node implements Serializable {
transient public final AtomicReference edit;
public final Object[] array;
diff --git a/src/jvm/clojure/lang/Range.java b/src/jvm/clojure/lang/Range.java
index 4f638e9f97..9408421da0 100644
--- a/src/jvm/clojure/lang/Range.java
+++ b/src/jvm/clojure/lang/Range.java
@@ -18,6 +18,8 @@
*/
public class Range extends ASeq implements IChunkedSeq, IReduce {
+private static final long serialVersionUID = -71973733672395145L;
+
private static final int CHUNK_SIZE = 32;
// Invariants guarantee this is never an "empty" seq
diff --git a/src/jvm/clojure/lang/Ratio.java b/src/jvm/clojure/lang/Ratio.java
index 6c7a9bb6e2..02af3fdea2 100644
--- a/src/jvm/clojure/lang/Ratio.java
+++ b/src/jvm/clojure/lang/Ratio.java
@@ -17,6 +17,9 @@
import java.math.MathContext;
public class Ratio extends Number implements Comparable{
+
+private static final long serialVersionUID = -576272795628662988L;
+
final public BigInteger numerator;
final public BigInteger denominator;
diff --git a/src/jvm/clojure/lang/Repeat.java b/src/jvm/clojure/lang/Repeat.java
index b874bea23f..934163902e 100644
--- a/src/jvm/clojure/lang/Repeat.java
+++ b/src/jvm/clojure/lang/Repeat.java
@@ -14,6 +14,8 @@
public class Repeat extends ASeq implements IReduce, IDrop {
+private static final long serialVersionUID = -5140377547192202551L;
+
private static final long INFINITE = -1;
private final long count; // always INFINITE or >0
diff --git a/src/jvm/clojure/lang/RestFn.java b/src/jvm/clojure/lang/RestFn.java
index 98646835c3..f6d844302a 100644
--- a/src/jvm/clojure/lang/RestFn.java
+++ b/src/jvm/clojure/lang/RestFn.java
@@ -10,6 +10,9 @@
package clojure.lang;
public abstract class RestFn extends AFunction{
+
+private static final long serialVersionUID = -4319097849151802009L;
+
abstract public int getRequiredArity();
protected Object doInvoke(Object args) {
diff --git a/src/jvm/clojure/lang/StringSeq.java b/src/jvm/clojure/lang/StringSeq.java
index cc93dea55b..3163380898 100644
--- a/src/jvm/clojure/lang/StringSeq.java
+++ b/src/jvm/clojure/lang/StringSeq.java
@@ -16,6 +16,9 @@
import java.util.NoSuchElementException;
public class StringSeq extends ASeq implements IndexedSeq,IDrop,IReduceInit{
+
+private static final long serialVersionUID = 7975525539139301753L;
+
public final CharSequence s;
public final int i;
diff --git a/src/jvm/clojure/lang/Symbol.java b/src/jvm/clojure/lang/Symbol.java
index 630ef1bb4d..5957bbebbb 100644
--- a/src/jvm/clojure/lang/Symbol.java
+++ b/src/jvm/clojure/lang/Symbol.java
@@ -17,6 +17,9 @@
public class Symbol extends AFn implements IObj, Comparable, Named, Serializable, IHashEq{
+
+private static final long serialVersionUID = 1191039485148212259L;
+
final String ns;
final String name;
private int _hasheq;
diff --git a/src/jvm/clojure/lang/Var.java b/src/jvm/clojure/lang/Var.java
index 787661926c..96f0cee635 100644
--- a/src/jvm/clojure/lang/Var.java
+++ b/src/jvm/clojure/lang/Var.java
@@ -19,6 +19,8 @@
public final class Var extends ARef implements IFn, IRef, Settable, Serializable{
+private static final long serialVersionUID = 8368961370796295279L;
+
static class TBox{
volatile Object val;
From 48e3292bb75015616ebce32de85428b71ac8dc1c Mon Sep 17 00:00:00 2001
From: Fogus
Date: Wed, 1 Jun 2022 07:44:05 -0400
Subject: [PATCH 119/285] CLJ-2712: Revert "fix AOT bug preventing overriding
of clojure.core functions"
This reverts commit 59889fdeb7ef7f4f73e13fa6ecb627f62b7d2adb.
Existing code handles the condition where a namespace mapping shadows an existing core name but does not handle the case where a var will shadow a future core var. New code to handle all cases is available in CLJ-2711.
---
src/jvm/clojure/lang/Compiler.java | 21 ++-------------------
1 file changed, 2 insertions(+), 19 deletions(-)
diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java
index 12cf1e5623..758108e8bc 100644
--- a/src/jvm/clojure/lang/Compiler.java
+++ b/src/jvm/clojure/lang/Compiler.java
@@ -410,7 +410,6 @@ static class DefExpr implements Expr{
public final Expr meta;
public final boolean initProvided;
public final boolean isDynamic;
- public final boolean shadowsCoreMapping;
public final String source;
public final int line;
public final int column;
@@ -419,9 +418,8 @@ static class DefExpr implements Expr{
final static Method setMetaMethod = Method.getMethod("void setMeta(clojure.lang.IPersistentMap)");
final static Method setDynamicMethod = Method.getMethod("clojure.lang.Var setDynamic(boolean)");
final static Method symintern = Method.getMethod("clojure.lang.Symbol intern(String, String)");
- final static Method internVar = Method.getMethod("clojure.lang.Var refer(clojure.lang.Symbol, clojure.lang.Var)");
- public DefExpr(String source, int line, int column, Var var, Expr init, Expr meta, boolean initProvided, boolean isDynamic, boolean shadowsCoreMapping){
+ public DefExpr(String source, int line, int column, Var var, Expr init, Expr meta, boolean initProvided, boolean isDynamic){
this.source = source;
this.line = line;
this.column = column;
@@ -429,7 +427,6 @@ public DefExpr(String source, int line, int column, Var var, Expr init, Expr met
this.init = init;
this.meta = meta;
this.isDynamic = isDynamic;
- this.shadowsCoreMapping = shadowsCoreMapping;
this.initProvided = initProvided;
}
@@ -475,18 +472,6 @@ public Object eval() {
public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
objx.emitVar(gen, var);
-
- if (shadowsCoreMapping)
- {
- gen.dup();
- gen.getField(VAR_TYPE, "ns", NS_TYPE);
- gen.swap();
- gen.dup();
- gen.getField(VAR_TYPE, "sym", SYMBOL_TYPE);
- gen.swap();
- gen.invokeVirtual(NS_TYPE, internVar);
- }
-
if(isDynamic)
{
gen.push(isDynamic);
@@ -544,13 +529,11 @@ else if(!(RT.second(form) instanceof Symbol))
Var v = lookupVar(sym, true);
if(v == null)
throw Util.runtimeException("Can't refer to qualified var that doesn't exist");
- boolean shadowsCoreMapping = false;
if(!v.ns.equals(currentNS()))
{
if(sym.ns == null)
{
v = currentNS().intern(sym);
- shadowsCoreMapping = true;
registerVar(v);
}
// throw Util.runtimeException("Name conflict, can't def " + sym + " because namespace: " + currentNS().name +
@@ -594,7 +577,7 @@ else if(!(RT.second(form) instanceof Symbol))
Expr meta = mm.count()==0 ? null:analyze(context == C.EVAL ? context : C.EXPRESSION, mm);
return new DefExpr((String) SOURCE.deref(), lineDeref(), columnDeref(),
v, analyze(context == C.EVAL ? context : C.EXPRESSION, RT.third(form), v.sym.name),
- meta, RT.count(form) == 3, isDynamic, shadowsCoreMapping);
+ meta, RT.count(form) == 3, isDynamic);
}
}
}
From 1e8fb8f51796b71d61a2992d4e2661827770158b Mon Sep 17 00:00:00 2001
From: Fogus
Date: Wed, 8 Jun 2022 11:19:17 -0400
Subject: [PATCH 120/285] CLJ-2711: Implements namespace interning policy such
that whenever interned vars (i.e. a mapping of sym => #'ns/sym in namespace
ns) are defined, they cannot ever be replaced via refer or intern. Any
attempt to replace interened vars will result in a warning and recommendation
to use ns-unmap first.
---
src/jvm/clojure/lang/Namespace.java | 75 ++++++++++++++++++++---------
test/clojure/test_clojure/rt.clj | 43 ++++++++---------
2 files changed, 74 insertions(+), 44 deletions(-)
diff --git a/src/jvm/clojure/lang/Namespace.java b/src/jvm/clojure/lang/Namespace.java
index 7fd465fc42..359815778c 100644
--- a/src/jvm/clojure/lang/Namespace.java
+++ b/src/jvm/clojure/lang/Namespace.java
@@ -50,11 +50,22 @@ public IPersistentMap getMappings(){
return mappings.get();
}
+/**
+ * An interned mapping is one where a var's ns matches the current ns and its sym matches the mapping key.
+ * Once established, interned mappings should never change.
+ */
+private boolean isInternedMapping(Symbol sym, Object o){
+ return(o instanceof Var &&
+ ((Var) o).ns == this &&
+ ((Var) o).sym.equals(sym));
+}
+
public Var intern(Symbol sym){
if(sym.ns != null)
{
throw new IllegalArgumentException("Can't intern namespace-qualified symbol");
}
+
IPersistentMap map = getMappings();
Object o;
Var v = null;
@@ -66,32 +77,50 @@ public Var intern(Symbol sym){
mappings.compareAndSet(map, newMap);
map = getMappings();
}
- if(o instanceof Var && ((Var) o).ns == this)
+ if(isInternedMapping(sym, o))
return (Var) o;
if(v == null)
v = new Var(this, sym);
- warnOrFailOnReplace(sym, o, v);
-
+ if(checkReplacement(sym, o, v)){
+ while (!mappings.compareAndSet(map, map.assoc(sym, v)))
+ map = getMappings();
- while(!mappings.compareAndSet(map, map.assoc(sym, v)))
- map = getMappings();
+ return v;
+ }
- return v;
+ return (Var) o;
}
-private void warnOrFailOnReplace(Symbol sym, Object o, Object v){
- if (o instanceof Var)
- {
- Namespace ns = ((Var)o).ns;
- if (ns == this || (v instanceof Var && ((Var)v).ns == RT.CLOJURE_NS))
- return;
- if (ns != RT.CLOJURE_NS)
- throw new IllegalStateException(sym + " already refers to: " + o + " in namespace: " + name);
- }
- RT.errPrintWriter().println("WARNING: " + sym + " already refers to: " + o + " in namespace: " + name
- + ", being replaced by: " + v);
+/*
+ This method checks if a namespace's mapping is applicable and warns on problematic cases.
+ It will return a boolean indicating if a mapping is replaceable.
+ The semantics of what constitutes a legal replacement mapping is summarized as follows:
+
+| classification | in namespace ns | newval = anything other than ns/name | newval = ns/name |
+|----------------+------------------------+--------------------------------------+-------------------------------------|
+| native mapping | name -> ns/name | no replace, warn-if newval not-core | no replace, warn-if newval not-core |
+| alias mapping | name -> other/whatever | warn + replace | warn + replace |
+*/
+private boolean checkReplacement(Symbol sym, Object old, Object neu){
+ if(old instanceof Var) {
+ Namespace ons = ((Var)old).ns;
+ Namespace nns = neu instanceof Var ? ((Var) neu).ns : null;
+
+ if(isInternedMapping(sym, old)){
+ if(nns != RT.CLOJURE_NS){
+ RT.errPrintWriter().println("REJECTED: attempt to replace interned var "
+ + old + " with " + neu + " in " + name + ", you must ns-unmap first");
+ return false;
+ }
+ else
+ return false;
+ }
+ }
+ RT.errPrintWriter().println("WARNING: " + sym + " already refers to: " + old + " in namespace: " + name
+ + ", being replaced by: " + neu);
+ return true;
}
Object reference(Symbol sym, Object val){
@@ -99,6 +128,7 @@ Object reference(Symbol sym, Object val){
{
throw new IllegalArgumentException("Can't intern namespace-qualified symbol");
}
+
IPersistentMap map = getMappings();
Object o;
while((o = map.valAt(sym)) == null)
@@ -110,13 +140,14 @@ Object reference(Symbol sym, Object val){
if(o == val)
return o;
- warnOrFailOnReplace(sym, o, val);
-
- while(!mappings.compareAndSet(map, map.assoc(sym, val)))
- map = getMappings();
+ if(checkReplacement(sym, o, val)){
+ while (!mappings.compareAndSet(map, map.assoc(sym, val)))
+ map = getMappings();
- return val;
+ return val;
+ }
+ return o;
}
public static boolean areDifferentInstancesOfSameClassName(Class cls1, Class cls2) {
diff --git a/test/clojure/test_clojure/rt.clj b/test/clojure/test_clojure/rt.clj
index 39526975f8..298fcb00da 100644
--- a/test/clojure/test_clojure/rt.clj
+++ b/test/clojure/test_clojure/rt.clj
@@ -9,7 +9,8 @@
; Author: Stuart Halloway
(ns clojure.test-clojure.rt
- (:require clojure.set)
+ (:require [clojure.string :as string]
+ clojure.set)
(:use clojure.test clojure.test-helper))
(defn bare-rt-print
@@ -75,31 +76,29 @@
(.bindRoot #'example-var 0)
(is (not (contains? (meta #'example-var) :macro))))
-(deftest last-var-wins-for-core
+(deftest ns-intern-policies
(testing "you can replace a core name, with warning"
(let [ns (temp-ns)
- replacement (gensym)]
- (with-err-string-writer (intern ns 'prefers replacement))
+ replacement (gensym)
+ e1 (with-err-string-writer (intern ns 'prefers replacement))]
+ (is (string/starts-with? e1 "WARNING"))
(is (= replacement @('prefers (ns-publics ns))))))
- (testing "you can replace a name you defined before"
+ (testing "you can replace a defined alias"
(let [ns (temp-ns)
s (gensym)
v1 (intern ns 'foo s)
- v2 (intern ns 'bar s)]
- (with-err-string-writer (.refer ns 'flatten v1))
- (.refer ns 'flatten v2)
+ v2 (intern ns 'bar s)
+ e1 (with-err-string-writer (.refer ns 'flatten v1))
+ e2 (with-err-string-writer (.refer ns 'flatten v2))]
+ (is (string/starts-with? e1 "WARNING"))
+ (is (string/starts-with? e2 "WARNING"))
(is (= v2 (ns-resolve ns 'flatten)))))
- (testing "you cannot intern over an existing non-core name"
- (let [ns (temp-ns 'clojure.set)
- replacement (gensym)]
- (is (thrown? IllegalStateException
- (intern ns 'subset? replacement)))
- (is (nil? ('subset? (ns-publics ns))))
- (is (= #'clojure.set/subset? ('subset? (ns-refers ns))))))
- (testing "you cannot refer over an existing non-core name"
- (let [ns (temp-ns 'clojure.set)
- replacement (gensym)]
- (is (thrown? IllegalStateException
- (.refer ns 'subset? #'clojure.set/intersection)))
- (is (nil? ('subset? (ns-publics ns))))
- (is (= #'clojure.set/subset? ('subset? (ns-refers ns)))))))
+ (testing "you cannot replace an interned var"
+ (let [ns1 (temp-ns)
+ ns2 (temp-ns)
+ v1 (intern ns1 'foo 1)
+ v2 (intern ns2 'foo 2)
+ e1 (with-err-string-writer (.refer ns1 'foo v2))]
+ (is (string/starts-with? e1 "REJECTED"))
+ (is (= v1 (ns-resolve ns1 'foo))))))
+
From e6fce5a42ba78fadcde00186c0b0c3cd00f45435 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Tue, 10 May 2022 11:52:52 -0500
Subject: [PATCH 121/285] CLJ-1872 Extend empty? to counted? colls that arent
seqable, such as transients
---
src/clj/clojure/core.clj | 17 ++++++++++-------
test/clojure/test_clojure/sequences.clj | 10 ++++++++--
2 files changed, 18 insertions(+), 9 deletions(-)
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index db5641339d..1e48fcc9c6 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -6244,13 +6244,6 @@ fails, attempts to require sym's namespace and retries."
([m k f x y z & more]
(assoc m k (apply f (get m k) x y z more))))
-(defn empty?
- "Returns true if coll has no items - same as (not (seq coll)).
- Please use the idiom (seq x) rather than (not (empty? x))"
- {:added "1.0"
- :static true}
- [coll] (not (seq coll)))
-
(defn coll?
"Returns true if x implements IPersistentCollection"
{:added "1.0"
@@ -6306,6 +6299,16 @@ fails, attempts to require sym's namespace and retries."
:static true}
[coll] (instance? clojure.lang.Counted coll))
+(defn empty?
+ "Returns true if coll has no items. To check the emptiness of a seq,
+ please use the idiom (seq x) rather than (not (empty? x))"
+ {:added "1.0"
+ :static true}
+ [coll]
+ (if (counted? coll)
+ (zero? (count coll))
+ (not (seq coll))))
+
(defn reversible?
"Returns true if coll implements Reversible"
{:added "1.0"
diff --git a/test/clojure/test_clojure/sequences.clj b/test/clojure/test_clojure/sequences.clj
index c684a6b298..7565c6fb8a 100644
--- a/test/clojure/test_clojure/sequences.clj
+++ b/test/clojure/test_clojure/sequences.clj
@@ -1136,7 +1136,10 @@
{}
#{}
""
- (into-array []) )
+ (into-array [])
+ (transient [])
+ (transient #{})
+ (transient {}))
(are [x] (not (empty? x))
'(1 2)
@@ -1145,7 +1148,10 @@
{:a 1 :b 2}
#{1 2}
"abc"
- (into-array [1 2]) ))
+ (into-array [1 2])
+ (transient [1])
+ (transient #{1})
+ (transient {1 2})))
(deftest test-every?
From 7b738af51c1ce52b91d7e9fb7399289a349d18c6 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 30 Jun 2022 15:56:35 -0500
Subject: [PATCH 122/285] Fix javadoc html entity in IDrop
---
src/jvm/clojure/lang/IDrop.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/jvm/clojure/lang/IDrop.java b/src/jvm/clojure/lang/IDrop.java
index ce4341ce02..336c0a8270 100644
--- a/src/jvm/clojure/lang/IDrop.java
+++ b/src/jvm/clojure/lang/IDrop.java
@@ -20,7 +20,7 @@ public interface IDrop{
* useful if the returned coll implements IDrop for subsequent use in a
* partition-like scenario.
*
- * If n is <= 0, return this.
+ * If n is <= 0, return this.
* If n drops to or past the end of the collection, return null.
*
* @param n Items to drop
From 52bb71bb615e2ad2cd75b3d47fce541d5b37c1ec Mon Sep 17 00:00:00 2001
From: Clojure Build
Date: Thu, 30 Jun 2022 16:07:41 -0500
Subject: [PATCH 123/285] [maven-release-plugin] prepare release
clojure-1.12.0-alpha1
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 2dd37faaad..709a83e952 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.12.0-master-SNAPSHOT
+ 1.12.0-alpha1
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- HEAD
+ clojure-1.12.0-alpha1
From 5ffe3833508495ca7c635d47ad7a1c8b820eab76 Mon Sep 17 00:00:00 2001
From: Clojure Build
Date: Thu, 30 Jun 2022 16:07:41 -0500
Subject: [PATCH 124/285] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 709a83e952..2dd37faaad 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.12.0-alpha1
+ 1.12.0-master-SNAPSHOT
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- clojure-1.12.0-alpha1
+ HEAD
From ca1768bcefbea46c43c659780501aa7bfda94ddf Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Tue, 6 Sep 2022 09:19:56 -0500
Subject: [PATCH 125/285] CLJ-2721 Fix invalid arg order when adding meta to
non-long range
---
src/jvm/clojure/lang/Range.java | 2 +-
test/clojure/test_clojure/sequences.clj | 9 +++++++++
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/jvm/clojure/lang/Range.java b/src/jvm/clojure/lang/Range.java
index 9408421da0..1e4fbf6e01 100644
--- a/src/jvm/clojure/lang/Range.java
+++ b/src/jvm/clojure/lang/Range.java
@@ -101,7 +101,7 @@ public static ISeq create(final Object start, Object end, Object step) {
public Obj withMeta(IPersistentMap meta){
if(meta == _meta)
return this;
- return new Range(meta, end, start, step, boundsCheck, _chunk, _chunkNext);
+ return new Range(meta, start, end, step, boundsCheck, _chunk, _chunkNext);
}
public Object first(){
diff --git a/test/clojure/test_clojure/sequences.clj b/test/clojure/test_clojure/sequences.clj
index 7565c6fb8a..7a7e6d413c 100644
--- a/test/clojure/test_clojure/sequences.clj
+++ b/test/clojure/test_clojure/sequences.clj
@@ -1075,6 +1075,15 @@
(reduce + (iterator-seq (.iterator (range 100)))) 4950
(reduce + (iterator-seq (.iterator (range 0.0 100.0 1.0)))) 4950.0 ))
+(deftest range-meta
+ (are [r] (= r (with-meta r {:a 1}))
+ (range 10)
+ (range 5 10)
+ (range 5 10 1)
+ (range 10.0)
+ (range 5.0 10.0)
+ (range 5.0 10.0 1.0)))
+
(deftest range-test
(let [threads 10
n 1000
From 09887382ac5f0bde2ceda0c137cd4aad94b2225d Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 18 Aug 2022 13:28:46 -0500
Subject: [PATCH 126/285] CLJ-2718 Fix bug in drop of repeat that goes to or
past end of sequence
---
src/jvm/clojure/lang/Repeat.java | 11 +++++++----
test/clojure/test_clojure/sequences.clj | 7 ++++++-
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/src/jvm/clojure/lang/Repeat.java b/src/jvm/clojure/lang/Repeat.java
index 934163902e..1190b1e5d7 100644
--- a/src/jvm/clojure/lang/Repeat.java
+++ b/src/jvm/clojure/lang/Repeat.java
@@ -100,12 +100,15 @@ public Object reduce(IFn f, Object start){
}
public Sequential drop(int n) {
- if(count > 1) {
- return new Repeat(count-n, val);
- } else if(count == INFINITE) {
+ if(count == INFINITE) {
return this;
} else {
- return null;
+ long droppedCount = count-n;
+ if(droppedCount > 0) {
+ return new Repeat(droppedCount, val);
+ } else {
+ return null;
+ }
}
}
diff --git a/test/clojure/test_clojure/sequences.clj b/test/clojure/test_clojure/sequences.clj
index 7a7e6d413c..fd3382ebd8 100644
--- a/test/clojure/test_clojure/sequences.clj
+++ b/test/clojure/test_clojure/sequences.clj
@@ -1003,7 +1003,12 @@
() '(1 2)
[] [1 2]
{} {:a 1 :b 2}
- #{} #{1 2} ))
+ #{} #{1 2})
+
+ ; CLJ-2718
+ (is (= '(:a) (drop 1 (repeat 2 :a))))
+ (is (= () (drop 2 (repeat 2 :a))))
+ (is (= () (drop 3 (repeat 2 :a)))))
(defspec longrange-equals-range 1000
(prop/for-all [start gen/int
From 527b330045ef35b47a968d80ed3dc4999cfa2623 Mon Sep 17 00:00:00 2001
From: Steve Miner
Date: Wed, 12 Oct 2022 13:21:47 -0500
Subject: [PATCH 127/285] CLJ-2715 partitionv - group last padded partition for
inclusion in seq of partitions
---
src/clj/clojure/core.clj | 2 +-
test/clojure/test_clojure/sequences.clj | 4 ++++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 1e48fcc9c6..a146fcfa63 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -7375,7 +7375,7 @@ fails, attempts to require sym's namespace and retries."
(let [p (into [] (take n) s)]
(if (= n (count p))
(cons p (partitionv n step pad (nthrest s step)))
- (into [] (take n) (concat p pad))))))))
+ (list (into [] (take n) (concat p pad)))))))))
(defn partitionv-all
"Returns a lazy sequence of vector partitions, but may include
diff --git a/test/clojure/test_clojure/sequences.clj b/test/clojure/test_clojure/sequences.clj
index fd3382ebd8..bc79c1f62e 100644
--- a/test/clojure/test_clojure/sequences.clj
+++ b/test/clojure/test_clojure/sequences.clj
@@ -781,6 +781,8 @@
(partition 5 [1 2 3]) ()
+ (partition 4 4 [0 0 0] (range 10)) '((0 1 2 3) (4 5 6 7) (8 9 0 0))
+
; (partition 0 [1 2 3]) (repeat nil) ; infinite sequence of nil
(partition -1 [1 2 3]) ()
(partition -2 [1 2 3]) () )
@@ -809,6 +811,8 @@
(partitionv 1 []) ()
(partitionv 1 [1 2 3]) '((1) (2) (3))
+ (partitionv 4 4 [0 0 0] (range 10)) '([0 1 2 3] [4 5 6 7] [8 9 0 0])
+
(partitionv 5 [1 2 3]) ()
(partitionv -1 [1 2 3]) ()
From cdba5c88a6dca17a37cd652357e044fc351b0172 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Tue, 11 Jan 2022 08:37:03 -0600
Subject: [PATCH 128/285] CLJ-2683 Fix with-open macro to not qualify the close
method on expansion
---
src/clj/clojure/core.clj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index a146fcfa63..00ccda1072 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -3852,7 +3852,7 @@
(try
(with-open ~(subvec bindings 2) ~@body)
(finally
- (. ~(bindings 0) close))))
+ (. ~(bindings 0) ~'close))))
:else (throw (IllegalArgumentException.
"with-open only allows Symbols in bindings"))))
From b9a51c4232fbc600fea86cd737d8d7a9e6c8c653 Mon Sep 17 00:00:00 2001
From: Fogus
Date: Tue, 10 May 2022 14:28:37 -0400
Subject: [PATCH 129/285] CLJ-2709: Check arguments to range to see if they are
fixed-precision integers and if so upcast them to Long when creating the
LongRange instance.
---
src/clj/clojure/core.clj | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 00ccda1072..d43135dee6 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -3045,15 +3045,15 @@
([]
(iterate inc' 0))
([end]
- (if (instance? Long end)
+ (if (int? end)
(clojure.lang.LongRange/create end)
(clojure.lang.Range/create end)))
([start end]
- (if (and (instance? Long start) (instance? Long end))
+ (if (and (int? start) (int? end))
(clojure.lang.LongRange/create start end)
(clojure.lang.Range/create start end)))
([start end step]
- (if (and (instance? Long start) (instance? Long end) (instance? Long step))
+ (if (and (int? start) (int? end) (int? step))
(clojure.lang.LongRange/create start end step)
(clojure.lang.Range/create start end step))))
From f296aa6c27eb40b7012534bee8de353591f3fa56 Mon Sep 17 00:00:00 2001
From: Fogus
Date: Tue, 20 Sep 2022 16:43:29 -0400
Subject: [PATCH 130/285] CLJ-2724: fixed improper do-copy hint on output to
match multimethod match type
---
src/clj/clojure/java/io.clj | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/clj/clojure/java/io.clj b/src/clj/clojure/java/io.clj
index 72a30ed7a6..f0715abfd4 100644
--- a/src/clj/clojure/java/io.clj
+++ b/src/clj/clojure/java/io.clj
@@ -21,6 +21,8 @@
CharArrayReader Closeable)
(java.net URI URL MalformedURLException Socket URLDecoder URLEncoder)))
+(set! *warn-on-reflection* true)
+
(def
^{:doc "Type object for a Java primitive byte array."
:private true
@@ -385,7 +387,7 @@
(defmethod do-copy [byte-array-type Writer] [^"[B" input ^Writer output opts]
(do-copy (ByteArrayInputStream. input) output opts))
-(defmethod do-copy [byte-array-type File] [^"[B" input ^Writer output opts]
+(defmethod do-copy [byte-array-type File] [^"[B" input ^File output opts]
(do-copy (ByteArrayInputStream. input) output opts))
(defn copy
From 9f8628f2c20fb9b7bcef35bb9e57b92ffe9c348f Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 29 Sep 2022 17:08:58 -0500
Subject: [PATCH 131/285] CLJ-2726 #uuid data reader throws
IllegalArgumentException rather than AssertionError for better error
reporting as reader error
---
src/clj/clojure/uuid.clj | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/clj/clojure/uuid.clj b/src/clj/clojure/uuid.clj
index dbaa9d141e..4ffbdf60d5 100644
--- a/src/clj/clojure/uuid.clj
+++ b/src/clj/clojure/uuid.clj
@@ -9,8 +9,9 @@
(ns clojure.uuid)
(defn- default-uuid-reader [form]
- {:pre [(string? form)]}
- (java.util.UUID/fromString form))
+ (if (string? form)
+ (java.util.UUID/fromString form)
+ (throw (IllegalArgumentException. "#uuid data reader expected string"))))
(defmethod print-method java.util.UUID [uuid ^java.io.Writer w]
(.write w (str "#uuid \"" (str uuid) "\"")))
From 861a5efc136dc4ca52066dcf38f0965367631e7d Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 8 Dec 2022 11:09:19 -0600
Subject: [PATCH 132/285] CLJ-2739 Fix error message when function called with
arg count >20
---
src/jvm/clojure/lang/ArityException.java | 2 +-
test/clojure/test_clojure/keywords.clj | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/jvm/clojure/lang/ArityException.java b/src/jvm/clojure/lang/ArityException.java
index 5c0f384f1a..89d2c8f4fe 100644
--- a/src/jvm/clojure/lang/ArityException.java
+++ b/src/jvm/clojure/lang/ArityException.java
@@ -26,7 +26,7 @@ public ArityException(int actual, String name) {
}
public ArityException(int actual, String name, Throwable cause) {
- super("Wrong number of args (" + actual + ") passed to: " + Compiler.demunge(name), cause);
+ super("Wrong number of args (" + (actual <= 20 ? actual : "> 20") + ") passed to: " + Compiler.demunge(name), cause);
this.actual = actual;
this.name = name;
}
diff --git a/test/clojure/test_clojure/keywords.clj b/test/clojure/test_clojure/keywords.clj
index 614fbc14e2..b486a067ab 100644
--- a/test/clojure/test_clojure/keywords.clj
+++ b/test/clojure/test_clojure/keywords.clj
@@ -27,5 +27,5 @@
(deftest arity-exceptions
(is (thrown-with-msg? IllegalArgumentException #"Wrong number of args \(0\) passed to: :kw" (:kw)))
(is (thrown-with-msg? IllegalArgumentException #"Wrong number of args \(20\) passed to: :foo/bar" (apply :foo/bar (range 20))))
- (is (thrown-with-msg? IllegalArgumentException #"Wrong number of args \(21\) passed to: :foo/bar" (apply :foo/bar (range 21))))
- (is (thrown-with-msg? IllegalArgumentException #"Wrong number of args \(22\) passed to: :foo/bar" (apply :foo/bar (range 22)))))
+ (is (thrown-with-msg? IllegalArgumentException #"Wrong number of args \(> 20\) passed to: :foo/bar" (apply :foo/bar (range 21))))
+ (is (thrown-with-msg? IllegalArgumentException #"Wrong number of args \(> 20\) passed to: :foo/bar" (apply :foo/bar (range 22)))))
From adfad027ec5b0ade65f507dbeb917710d95e9949 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Mon, 5 Dec 2022 16:40:01 -0600
Subject: [PATCH 133/285] CLJ-2740 Eliminate unneeded call in new fast drop
support
---
src/jvm/clojure/lang/PersistentVector.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/jvm/clojure/lang/PersistentVector.java b/src/jvm/clojure/lang/PersistentVector.java
index 4b9f3260ad..973cb2a31a 100644
--- a/src/jvm/clojure/lang/PersistentVector.java
+++ b/src/jvm/clojure/lang/PersistentVector.java
@@ -500,7 +500,6 @@ public Sequential drop(int n) {
} else {
int i = this.i +o;
if(i < vec.cnt) { // in vec
- Object[] array = vec.arrayFor(i);
int newOffset = i%32;
return new ChunkedSeq(vec, vec.arrayFor(i), i-newOffset, newOffset);
} else {
From 8adf5bce1ecb45a940f6b4233aff075be5448a5b Mon Sep 17 00:00:00 2001
From: Nicola Mometto
Date: Wed, 11 May 2022 20:34:19 +0100
Subject: [PATCH 134/285] CLJ-2521: hoist loop bodies in statement position
---
src/jvm/clojure/lang/Compiler.java | 2 +-
test/clojure/test_clojure/compilation.clj | 4 ++++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java
index 758108e8bc..b7d0dfff8c 100644
--- a/src/jvm/clojure/lang/Compiler.java
+++ b/src/jvm/clojure/lang/Compiler.java
@@ -6342,7 +6342,7 @@ public Expr parse(C context, Object frm) {
ISeq body = RT.next(RT.next(form));
if(context == C.EVAL
- || (context == C.EXPRESSION && isLoop))
+ || (context != C.RETURN && isLoop))
return analyze(context, RT.list(RT.list(FNONCE, PersistentVector.EMPTY, form)));
ObjMethod method = (ObjMethod) METHOD.deref();
diff --git a/test/clojure/test_clojure/compilation.clj b/test/clojure/test_clojure/compilation.clj
index 999d33f91b..3aae891759 100644
--- a/test/clojure/test_clojure/compilation.clj
+++ b/test/clojure/test_clojure/compilation.clj
@@ -443,3 +443,7 @@
(testing "CLJ-2580 Correctly calculate exit branches of case"
(is (zero? (let [d (case nil :x nil 0)] d)))
(is (nil? (let [d (case nil :x 0 nil)] d)))))
+
+(deftest CLJ-2521
+ (testing "CLJ-2521 Can nest loop->try when in statement position"
+ (is (= "ret" ((fn [] (loop [] (try "" (catch Throwable _))) "ret"))))))
From 1e835a9d9fb4498a5cf643df861565c07701b18b Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 15 Dec 2022 17:52:18 -0600
Subject: [PATCH 135/285] CLJ-2742 Revert range to use chunking as before the
IDrop changes in CLJ-2713
---
src/jvm/clojure/lang/LongRange.java | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/src/jvm/clojure/lang/LongRange.java b/src/jvm/clojure/lang/LongRange.java
index d1bfba2fe0..10381cc29b 100644
--- a/src/jvm/clojure/lang/LongRange.java
+++ b/src/jvm/clojure/lang/LongRange.java
@@ -116,16 +116,23 @@ public ISeq next() {
}
}
+private static final int CHUNK_SIZE = 32;
+
public IChunk chunkedFirst() {
- return new LongChunk(start, step, count);
+ return new LongChunk(start, step, Math.min(count, CHUNK_SIZE));
}
public ISeq chunkedNext() {
- return null;
+ return chunkedMore().seq();
}
public ISeq chunkedMore() {
- return PersistentList.EMPTY;
+ if(count <= CHUNK_SIZE) {
+ return PersistentList.EMPTY;
+ } else {
+ return LongRange.create(start + (step * CHUNK_SIZE), end, step);
+ }
+
}
public Sequential drop(int n) {
From b7d87dcd729628a2e80038d05023eb7c7786bb11 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Fri, 31 Mar 2023 09:49:21 -0500
Subject: [PATCH 136/285] CLJ-2757 clojure.java.basis - api for accessing basis
---
src/clj/clojure/java/basis.clj | 47 ++++++++++++++++++++++++++
src/clj/clojure/java/basis/impl.clj | 51 +++++++++++++++++++++++++++++
2 files changed, 98 insertions(+)
create mode 100644 src/clj/clojure/java/basis.clj
create mode 100644 src/clj/clojure/java/basis/impl.clj
diff --git a/src/clj/clojure/java/basis.clj b/src/clj/clojure/java/basis.clj
new file mode 100644
index 0000000000..9567801962
--- /dev/null
+++ b/src/clj/clojure/java/basis.clj
@@ -0,0 +1,47 @@
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html at the root of this distribution.
+; By using this software in any fashion, you are agreeing to be bound by
+; the terms of this license.
+; You must not remove this notice, or any other, from this software.
+
+(ns clojure.java.basis
+ "The lib basis includes which libraries and versions were loaded both
+ for direct dependencies and transitive dependencies, as well as the
+ classpath and possibly other information from the resolution process.
+ This basis will be known if the runtime was started by the Clojure CLI.
+
+ The Clojure CLI or tools.deps merge a set of deps maps (often from
+ deps.edn files). Additional runtime modifications are supplied via argmap
+ keys, provided via alias maps in the merged deps. Deps maps typically have
+ :paths, :deps, and :aliases keys.
+
+ The basis is a superset of merged deps.edn files with the following
+ additional keys:
+ :basis-config - params used to configure basis deps sources, can be
+ string path, deps map, nil, or :default
+ :root - default = loaded as a resource from tools.deps)
+ :user - default = ~/.clojure/deps.edn)
+ :project - default = ./deps.edn)
+ :extra - default = nil
+ :aliases - coll of keyword aliases to include during dep calculation
+ :argmap - effective argmap (after resolving and merging argmaps from aliases)
+ :libs - map of lib to coord for all included libraries
+ :classpath - classpath map, keys are paths (to directory or .jar), values
+ are maps with source identifier (either :lib-name or :path-key)
+ :classpath-roots - vector of paths in classpath order (keys of :classpath)"
+ (:require
+ [clojure.java.basis.impl :as impl]))
+
+(defn initial-basis
+ "Initial runtime basis at launch, nil if unknown (process not started by CLI)"
+ {:added "1.12"}
+ []
+ @impl/init-basis)
+
+(defn current-basis
+ "Return the current basis, which may have been modified since runtime launch."
+ {:added "1.12"}
+ []
+ @@impl/the-basis)
diff --git a/src/clj/clojure/java/basis/impl.clj b/src/clj/clojure/java/basis/impl.clj
new file mode 100644
index 0000000000..5402a9e201
--- /dev/null
+++ b/src/clj/clojure/java/basis/impl.clj
@@ -0,0 +1,51 @@
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html at the root of this distribution.
+; By using this software in any fashion, you are agreeing to be bound by
+; the terms of this license.
+; You must not remove this notice, or any other, from this software.
+
+(ns clojure.java.basis.impl
+ (:require
+ [clojure.edn :as edn]
+ [clojure.java.io :as jio])
+ (:import
+ [java.io PushbackReader]))
+
+(set! *warn-on-reflection* true)
+
+(defn- read-edn
+ "Coerce f to a reader via clojure.java.io/reader and read one edn value.
+ The reader should contain a single value. Empty input returns nil.
+ The reader will be read to EOF and closed."
+ [f]
+ (let [reader (jio/reader f)
+ EOF (Object.)]
+ (with-open [rdr (PushbackReader. reader)]
+ (let [val (edn/read {:default tagged-literal :eof EOF} rdr)]
+ (if (identical? EOF val)
+ nil
+ (if (not (identical? EOF (edn/read {:eof EOF} rdr)))
+ (throw (ex-info "Invalid file, expected edn to contain a single value." {}))
+ val))))))
+
+(defn- read-basis
+ "Read basis edn from basis file or throw"
+ [basis-file]
+ (when-let [f (jio/file basis-file)]
+ (when (.exists f)
+ (read-edn f))))
+
+;; delay construction until needed, access via initial-basis
+(def init-basis
+ (delay (read-basis (System/getProperty "clojure.basis"))))
+
+;; delay construction until needed, access via current-basis
+(def the-basis
+ (delay (atom @init-basis)))
+
+(defn update-basis!
+ "Update the runtime basis by applying f with args"
+ [f & args]
+ (apply swap! @the-basis f args))
From 65ea3e7688dfb492403f615cb0a5694f5ccefadc Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 6 Apr 2023 12:15:31 -0500
Subject: [PATCH 137/285] CLJ-2759 Java process api
---
src/clj/clojure/java/process.clj | 161 +++++++++++++++++++++++++++++++
1 file changed, 161 insertions(+)
create mode 100644 src/clj/clojure/java/process.clj
diff --git a/src/clj/clojure/java/process.clj b/src/clj/clojure/java/process.clj
new file mode 100644
index 0000000000..881b69413b
--- /dev/null
+++ b/src/clj/clojure/java/process.clj
@@ -0,0 +1,161 @@
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html at the root of this distribution.
+; By using this software in any fashion, you are agreeing to be bound by
+; the terms of this license.
+; You must not remove this notice, or any other, from this software.
+
+(ns clojure.java.process
+ "A process invocation API wrapping the Java process API.
+
+ The primary function here is 'start' which starts a process and handles the
+ streams as directed. It returns a map that contains keys to access the streams
+ (if available) and the Java Process object. It is also deref-able to wait for
+ process exit.
+
+ Helper functions are available to 'capture' the output of the process stdout
+ and to wait for an 'ok?' non-error exit. The 'exec' function handles the common
+ case of `start'ing a process, waiting for process exit, capture and return
+ stdout."
+ (:require
+ [clojure.java.io :as jio]
+ [clojure.string :as str])
+ (:import
+ [java.io StringWriter File]
+ [java.lang ProcessBuilder ProcessBuilder$Redirect Process]
+ [java.util List]
+ [clojure.lang IDeref IBlockingDeref]))
+
+(set! *warn-on-reflection* true)
+
+;; this is built into Java 9, backfilled here for Java 8
+(def ^:private ^File null-file
+ (delay
+ (jio/file
+ (if (.startsWith (System/getProperty "os.name") "Windows")
+ "NUL"
+ "/dev/null"))))
+
+(defn to-file
+ "Coerce f to a file per clojure.java.io/file and return a ProcessBuilder.Redirect writing to the file.
+ Set ':append' in opts to append. This can be passed to 'start' in :out or :err."
+ {:added "1.12"}
+ ^ProcessBuilder$Redirect [file & {:keys [append] :as opts}]
+ (let [f (jio/file file)]
+ (if append
+ (ProcessBuilder$Redirect/appendTo f)
+ (ProcessBuilder$Redirect/to f))))
+
+(defn from-file
+ "Coerce f to a file per clojure.java.io/file and return a ProcessBuilder.Redirect reading from the file.
+ This can be passed to 'start' in :in."
+ {:added "1.12"}
+ ^ProcessBuilder$Redirect [file]
+ (ProcessBuilder$Redirect/from (jio/file file)))
+
+(defn start
+ "Starts an external command as args and optional leading opts map:
+
+ :in - a ProcessBuilder.Redirect (default = :pipe) or :inherit
+ :out - a ProcessBuilder.Redirect (default = :pipe) or :inherit :discard
+ :err - a ProcessBuilder.Redirect (default = :pipe) or :inherit :discard :stdout
+ :dir - directory to run the command from, default=\".\"
+ :env - {env-var value} of environment variables (all strings)
+
+ Returns an ILookup containing the java.lang.Process in :process and the
+ streams :in :out :err. The map is also an IDeref that waits for process exit
+ and returns the exit code."
+ {:added "1.12"}
+ [& opts+args]
+ (let [[opts command] (if (map? (first opts+args))
+ [(first opts+args) (rest opts+args)]
+ [{} opts+args])
+ {:keys [in out err dir env]
+ :or {in :pipe, out :pipe, err :pipe, dir "."}} opts
+ pb (ProcessBuilder. ^List command)
+ to-redirect (fn to-redirect
+ [x]
+ (case x
+ :pipe ProcessBuilder$Redirect/PIPE
+ :inherit ProcessBuilder$Redirect/INHERIT
+ :discard (ProcessBuilder$Redirect/to @null-file)
+ ;; in Java 9+, just use ProcessBuilder$Redirect/DISCARD
+ x))]
+ (.directory pb (jio/file (or dir ".")))
+ (when in (.redirectInput pb ^ProcessBuilder$Redirect (to-redirect in)))
+ (when out (.redirectOutput pb ^ProcessBuilder$Redirect (to-redirect out)))
+ (cond
+ (= err :stdout) (.redirectErrorStream pb)
+ err (.redirectError pb ^ProcessBuilder$Redirect (to-redirect err)))
+ (when env
+ (let [pb-env (.environment pb)]
+ (run! (fn [[k v]] (.put pb-env k v)) env)))
+ (let [proc (.start pb)
+ m {:process proc
+ :in (.getOutputStream proc)
+ :out (.getInputStream proc)
+ :err (.getErrorStream proc)}]
+ (reify
+ clojure.lang.ILookup
+ (valAt [_ key] (get m key))
+ (valAt [_ key not-found] (get m key not-found))
+
+ IDeref
+ (deref [_] (.waitFor proc))
+
+ IBlockingDeref
+ (deref [_ timeout unit] (.waitFor proc timeout unit))))))
+
+(defn ok?
+ "Given the map returned from 'start', wait for the process to exit
+ and then return true on success"
+ {:added "1.12"}
+ [process-map]
+ (zero? (.waitFor ^Process (:process process-map))))
+
+(defn capture
+ "Read from input-stream until EOF and return a String (or nil if 0 length).
+ Takes same opts as clojure.java.io/copy - :buffer and :encoding"
+ {:added "1.12"}
+ [input-stream & opts]
+ (let [writer (StringWriter.)]
+ (apply jio/copy input-stream writer opts)
+ (let [s (str/trim (.toString writer))]
+ (when-not (zero? (.length s))
+ s))))
+
+(defn exec
+ "Execute a command and on successful exit, return the captured output,
+ else throw RuntimeException. Args are the same as 'start' and options
+ if supplied override the default 'exec' settings."
+ {:added "1.12"}
+ [& opts+args]
+ (let [[opts command] (if (map? (first opts+args))
+ [(first opts+args) (rest opts+args)]
+ [{} opts+args])
+ opts (merge opts {:err :inherit})]
+ (let [state (apply start opts command)
+ out-promise (promise)
+ capture-fn #(deliver out-promise (capture (:out state)))]
+ (doto (Thread. ^Runnable capture-fn) (.setDaemon true) (.start))
+ (if (ok? state)
+ @out-promise
+ (throw (RuntimeException. (str "Process failed with exit=" (.exitValue ^Process (:process state)))))))))
+
+(comment
+ ;; shell out and inherit the i/o
+ (start {:out :inherit, :err :stdout} "ls" "-l")
+
+ ;; write out and err to files, wait for process to exit, return exit code
+ @(start {:out (to-file "out") :err (to-file "err")} "ls" "-l")
+
+ ;; capture output to string
+ (-> (start "ls" "-l") :out capture)
+
+ ;; with exec
+ (exec "ls" "-l")
+
+ ;; read input from file
+ (exec {:in (from-file "deps.edn")} "wc" "-l")
+ )
From d1ae2781d71fc7f79c52e0730b0d65e89850da5c Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Fri, 31 Mar 2023 09:55:19 -0500
Subject: [PATCH 138/285] CLJ-2760 Add api to invoke an external function via
the Clojure CLI
---
src/clj/clojure/tools/deps/interop.clj | 61 ++++++++++++++++++++++++++
1 file changed, 61 insertions(+)
create mode 100644 src/clj/clojure/tools/deps/interop.clj
diff --git a/src/clj/clojure/tools/deps/interop.clj b/src/clj/clojure/tools/deps/interop.clj
new file mode 100644
index 0000000000..54449b731a
--- /dev/null
+++ b/src/clj/clojure/tools/deps/interop.clj
@@ -0,0 +1,61 @@
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html at the root of this distribution.
+; By using this software in any fashion, you are agreeing to be bound by
+; the terms of this license.
+; You must not remove this notice, or any other, from this software.
+(ns clojure.tools.deps.interop
+ "Functions for invoking Java processes and invoking tools via the Clojure CLI."
+ (:require
+ [clojure.java.process :as proc]
+ [clojure.edn :as edn]))
+
+(defn ^:dynamic invoke-tool
+ "Invoke tool using Clojure CLI. Args (one of :tool-alias or :tool-name, and :fn
+ are required):
+ :tool-alias - Tool alias to invoke (keyword)
+ :tool-name - Name of installed tool to invoke (string or symbol)
+ :fn - Function (symbol)
+ :args - map of args to pass to function
+
+ Options:
+ :command - CLI command, default=\"clojure\"
+ :preserve-envelope - if true, return the full invocation envelope, default=false"
+ {:added "1.12"}
+ [{:keys [tool-name tool-alias fn args command preserve-envelope]
+ :or {command "clojure", preserve-envelope false}
+ :as opts}]
+ (when-not (or tool-name tool-alias) (throw (ex-info "Either :tool-alias or :tool-name must be provided" (or opts {}))))
+ (when-not (symbol? fn) (throw (ex-info (str "fn should be a symbol " fn) (or opts {}))))
+ (let [args (assoc args :clojure.exec/invoke :fn)
+ _ (when (:debug opts) (println "args" args))
+ command-strs [command (str "-T" (or tool-alias tool-name)) (pr-str fn) (pr-str args)]
+ _ (when (:debug opts) (apply println "Invoking: " command-strs))
+ envelope (edn/read-string (apply proc/exec command-strs))]
+ (if preserve-envelope
+ envelope
+ (let [{:keys [tag val]} envelope
+ parsed-val (edn/read-string val)]
+ (if (= :ret tag)
+ parsed-val
+ (throw (ex-info (:cause parsed-val) (or parsed-val {}))))))))
+
+(comment
+ ;; regular invocation, should return {:hi :there}
+ (invoke-tool {:tool-alias :deps, :fn 'clojure.core/identity, :args {:hi :there}})
+
+ ;; invocation throws, should return throwable map data
+ (try
+ (invoke-tool {:tool-alias :deps, :fn 'clojure.core/+, :args {:fail :here}})
+ (catch clojure.lang.ExceptionInfo e (ex-data e)))
+
+ ;; capture stdout in returned envelope
+ (let [resp (invoke-tool {:tool-alias :deps,
+ :fn 'list
+ :args {:format :edn
+ :clojure.exec/out :capture}
+ :preserve-envelope true})]
+ (edn/read-string (:out resp)))
+
+ )
From d0175370c05ef12304aa46236012bc56059c3ddc Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 6 Apr 2023 12:20:38 -0500
Subject: [PATCH 139/285] CLJ-2761 - add-libs, add-lib, sync-deps for the repl
---
build.xml | 5 ++
src/clj/clojure/core.clj | 5 ++
src/clj/clojure/main.clj | 9 ++--
src/clj/clojure/repl/deps.clj | 94 +++++++++++++++++++++++++++++++++++
4 files changed, 109 insertions(+), 4 deletions(-)
create mode 100644 src/clj/clojure/repl/deps.clj
diff --git a/build.xml b/build.xml
index 34fd65b2c6..1464b69ed3 100644
--- a/build.xml
+++ b/build.xml
@@ -77,7 +77,10 @@
+
+
+
@@ -86,6 +89,8 @@
+
+
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index d43135dee6..caa1fb387e 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -6340,6 +6340,11 @@ fails, attempts to require sym's namespace and retries."
:added "1.0"}
*e)
+(def ^:dynamic
+ ^{:doc "Bound to true in a repl thread"
+ :added "1.12"}
+ *repl*)
+
(defn trampoline
"trampoline can be used to convert algorithms requiring mutual
recursion without stack consumption. Calls f with supplied args, if
diff --git a/src/clj/clojure/main.clj b/src/clj/clojure/main.clj
index e769ee56ea..bb72673107 100644
--- a/src/clj/clojure/main.clj
+++ b/src/clj/clojure/main.clj
@@ -73,7 +73,6 @@
" (" (.getFileName el) ":" (.getLineNumber el) ")")))
;;;;;;;;;;;;;;;;;;; end of redundantly copied from clojure.repl to avoid dep ;;;;;;;;;;;;;;
-
(defmacro with-bindings
"Executes body in the context of thread-local bindings for several vars
that often need to be set!: *ns* *warn-on-reflection* *math-context*
@@ -94,6 +93,7 @@
*unchecked-math* *unchecked-math*
*assert* *assert*
clojure.spec.alpha/*explain-out* clojure.spec.alpha/*explain-out*
+ *repl* true
*1 nil
*2 nil
*3 nil
@@ -356,7 +356,8 @@
by default when a new command-line REPL is started."} repl-requires
'[[clojure.repl :refer (source apropos dir pst doc find-doc)]
[clojure.java.javadoc :refer (javadoc)]
- [clojure.pprint :refer (pp pprint)]])
+ [clojure.pprint :refer (pp pprint)]
+ [clojure.repl.deps :refer (add-libs add-lib sync-deps)]])
(defmacro with-read-known
"Evaluates body with *read-eval* set to a \"known\" value,
@@ -454,7 +455,7 @@ by default when a new command-line REPL is started."} repl-requires
(prompt)
(flush)
(loop []
- (when-not
+ (when-not
(try (identical? (read-eval-print) request-exit)
(catch Throwable e
(caught e)
@@ -670,6 +671,6 @@ java -cp clojure.jar clojure.main -i init.clj script.clj args...")
(catch Throwable t
(report-error t :target "file")
(System/exit 1))))
- (finally
+ (finally
(flush))))
diff --git a/src/clj/clojure/repl/deps.clj b/src/clj/clojure/repl/deps.clj
new file mode 100644
index 0000000000..1ed8365a1c
--- /dev/null
+++ b/src/clj/clojure/repl/deps.clj
@@ -0,0 +1,94 @@
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html at the root of this distribution.
+; By using this software in any fashion, you are agreeing to be bound by
+; the terms of this license.
+; You must not remove this notice, or any other, from this software.
+(ns clojure.repl.deps
+ "clojure.repl.deps provides facilities for dynamically modifying the available
+ libraries in the runtime when running at the REPL, without restarting"
+ (:require
+ [clojure.java.io :as jio]
+ [clojure.java.basis :as basis]
+ [clojure.java.basis.impl :as basis-impl]
+ [clojure.tools.deps.interop :as tool])
+ (:import
+ [clojure.lang DynamicClassLoader]
+ [java.io File]))
+
+(set! *warn-on-reflection* true)
+
+(defn- add-loader-url
+ "Add url string or URL to the highest level DynamicClassLoader url set."
+ [url]
+ (let [u (if (string? url) (java.net.URL. url) url)
+ loader (loop [loader (.getContextClassLoader (Thread/currentThread))]
+ (let [parent (.getParent loader)]
+ (if (instance? DynamicClassLoader parent)
+ (recur parent)
+ loader)))]
+ (if (instance? DynamicClassLoader loader)
+ (.addURL ^DynamicClassLoader loader u)
+ (throw (IllegalAccessError. "Context classloader is not a DynamicClassLoader")))))
+
+(defn add-libs
+ "Given lib-coords, a map of lib to coord, will resolve all transitive deps for the libs
+ together and add them to the repl classpath, unlike separate calls to add-lib."
+ {:added "1.12"}
+ [lib-coords]
+ (when-not *repl* (throw (RuntimeException. "add-libs is only available at the REPL")))
+ (let [{:keys [libs] :as basis} (basis/current-basis)
+ current-libs (set (keys libs))
+ lib-coords (reduce-kv #(if (contains? current-libs %2) %1 (assoc %1 %2 %3))
+ {} lib-coords)]
+ (when-not (empty? lib-coords)
+ (let [procurer (dissoc basis [:basis-config :paths :deps :aliases :argmap :classpath :classpath-roots])
+ tool-args {:existing libs, :add lib-coords, :procurer procurer}
+ {:keys [added] :as _res} (tool/invoke-tool {:tool-alias :deps, :fn 'clojure.tools.deps/resolve-added-libs, :args tool-args})
+ ;_ (clojure.pprint/pprint _res)
+ paths (mapcat :paths (vals added))
+ urls (->> paths (map jio/file) (map #(.toURL ^File %)))]
+ (run! add-loader-url urls)
+ (basis-impl/update-basis! update :libs merge added)
+ (let [ret (-> added keys sort vec)]
+ (when (seq ret) ret))))))
+
+(defn add-lib
+ "Given a lib that is not yet on the repl classpath, make it available by
+ downloading the library if necessary and adding it to the classloader.
+ Libs already on the classpath are not updated. Requires a valid parent
+ DynamicClassLoader.
+
+ lib - symbol identifying a library, for Maven: groupId/artifactId
+ coord - optional map of location information specific to the procurer,
+ or latest if not supplied
+
+ Returns coll of libs loaded, including transitive (or nil if none).
+
+ For info on libs, coords, and versions, see:
+ https://clojure.org/reference/deps_and_cli"
+ {:added "1.12"}
+ ([lib coord]
+ (add-libs {lib coord}))
+ ([lib]
+ (let [procurer (select-keys (basis/current-basis) [:mvn/repos :mvn/local-repo])
+ coord (tool/invoke-tool {:tool-alias :deps
+ :fn 'clojure.tools.deps/find-latest-version
+ :args {:lib lib, :procurer procurer}})]
+ (if coord
+ (add-libs {lib coord})
+ (throw (ex-info (str "No version found for lib " lib) {}))))))
+
+(defn sync-deps
+ "Calls add-libs with any libs present in deps.edn but not yet present on the classpath.
+
+ :aliases - coll of alias keywords to use during the sync"
+ {:added "1.12"}
+ [& {:as opts}]
+ (let [{:keys [aliases]} opts
+ basis-config (:basis-config (basis/current-basis))
+ new-basis-config (update basis-config :aliases (fnil into []) aliases)
+ new-basis (tool/invoke-tool {:tool-alias :deps, :fn 'clojure.tools.deps/create-basis, :args new-basis-config})
+ new-libs (:libs new-basis)]
+ (add-libs new-libs)))
From bde257d1f44d65d623c687481d86a102f9142006 Mon Sep 17 00:00:00 2001
From: Clojure Build
Date: Fri, 14 Apr 2023 07:38:00 -0500
Subject: [PATCH 140/285] [maven-release-plugin] prepare release
clojure-1.12.0-alpha2
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 2dd37faaad..5e1118a13b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.12.0-master-SNAPSHOT
+ 1.12.0-alpha2
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- HEAD
+ clojure-1.12.0-alpha2
From 16a17e9f9bbc26e996dc893941cef09410c39f41 Mon Sep 17 00:00:00 2001
From: Clojure Build
Date: Fri, 14 Apr 2023 07:38:00 -0500
Subject: [PATCH 141/285] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 5e1118a13b..2dd37faaad 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.12.0-alpha2
+ 1.12.0-master-SNAPSHOT
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- clojure-1.12.0-alpha2
+ HEAD
From 4090f405466ea90bbaf3addbe41f0a6acb164dbb Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Tue, 18 Apr 2023 09:22:23 -0500
Subject: [PATCH 142/285] Revert "CLJ-2521: hoist loop bodies in statement
position"
This reverts commit 8adf5bce1ecb45a940f6b4233aff075be5448a5b.
---
src/jvm/clojure/lang/Compiler.java | 2 +-
test/clojure/test_clojure/compilation.clj | 4 ----
2 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java
index b7d0dfff8c..758108e8bc 100644
--- a/src/jvm/clojure/lang/Compiler.java
+++ b/src/jvm/clojure/lang/Compiler.java
@@ -6342,7 +6342,7 @@ public Expr parse(C context, Object frm) {
ISeq body = RT.next(RT.next(form));
if(context == C.EVAL
- || (context != C.RETURN && isLoop))
+ || (context == C.EXPRESSION && isLoop))
return analyze(context, RT.list(RT.list(FNONCE, PersistentVector.EMPTY, form)));
ObjMethod method = (ObjMethod) METHOD.deref();
diff --git a/test/clojure/test_clojure/compilation.clj b/test/clojure/test_clojure/compilation.clj
index 3aae891759..999d33f91b 100644
--- a/test/clojure/test_clojure/compilation.clj
+++ b/test/clojure/test_clojure/compilation.clj
@@ -443,7 +443,3 @@
(testing "CLJ-2580 Correctly calculate exit branches of case"
(is (zero? (let [d (case nil :x nil 0)] d)))
(is (nil? (let [d (case nil :x 0 nil)] d)))))
-
-(deftest CLJ-2521
- (testing "CLJ-2521 Can nest loop->try when in statement position"
- (is (= "ret" ((fn [] (loop [] (try "" (catch Throwable _))) "ret"))))))
From 1d704118d83d2550b050826b6fb505ab41a8ac7d Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 8 Jun 2023 14:18:13 -0500
Subject: [PATCH 143/285] Add workflows
---
.github/workflows/release.yml | 19 +++++++++++++++++++
.github/workflows/snapshot.yml | 8 ++++++++
.github/workflows/test.yml | 23 +++++++++++++++++++++++
3 files changed, 50 insertions(+)
create mode 100644 .github/workflows/release.yml
create mode 100644 .github/workflows/snapshot.yml
create mode 100644 .github/workflows/test.yml
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000000..e2718bd3ef
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,19 @@
+name: Release on demand
+
+on:
+ workflow_dispatch:
+ inputs:
+ releaseVersion:
+ description: "Version to release"
+ required: true
+ snapshotVersion:
+ description: "Snapshot version after release"
+ required: true
+
+jobs:
+ call-release:
+ uses: clojure/build.ci/.github/workflows/release.yml@master
+ with:
+ releaseVersion: ${{ github.event.inputs.releaseVersion }}
+ snapshotVersion: ${{ github.event.inputs.snapshotVersion }}
+ secrets: inherit
\ No newline at end of file
diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml
new file mode 100644
index 0000000000..24729578c1
--- /dev/null
+++ b/.github/workflows/snapshot.yml
@@ -0,0 +1,8 @@
+name: Snapshot on demand
+
+on: [workflow_dispatch]
+
+jobs:
+ call-snapshot:
+ uses: clojure/build.ci/.github/workflows/snapshot.yml@master
+ secrets: inherit
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000000..a6c3b6bf1a
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,23 @@
+name: Test
+
+on: [push]
+
+jobs:
+ test:
+ strategy:
+ matrix:
+ os: [ubuntu-latest] # macOS-latest, windows-latest]
+ java-version: ["8", "11", "17"]
+ distribution: ["temurin", "oracle", "corretto"]
+ profile: ["test-direct", "test-no-direct"]
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up Java
+ uses: actions/setup-java@v3
+ with:
+ java-version: ${{ matrix.java-version }}
+ distribution: ${{ matrix.distribution }}
+ cache: 'maven'
+ - name: Build with Maven
+ run: mvn -ntp -B -P${{ matrix.profile }} clean test
From 21150ff92afe61ac7b2c206d70870cc8e84b9801 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 8 Jun 2023 14:21:14 -0500
Subject: [PATCH 144/285] narrow test matrix JDK distributions
---
.github/workflows/test.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index a6c3b6bf1a..6d8bb247e3 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -8,7 +8,7 @@ jobs:
matrix:
os: [ubuntu-latest] # macOS-latest, windows-latest]
java-version: ["8", "11", "17"]
- distribution: ["temurin", "oracle", "corretto"]
+ distribution: ["temurin", "corretto"]
profile: ["test-direct", "test-no-direct"]
runs-on: ${{ matrix.os }}
steps:
From c72f833d40e19717c88bafa54c9e76e60520f11d Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Tue, 9 May 2023 16:23:58 -0500
Subject: [PATCH 145/285] CLJ-2772 Fix regression in drop nthrest nthnext on n
!= positive integer
---
src/clj/clojure/core.clj | 14 ++++--
src/jvm/clojure/lang/IDrop.java | 7 +--
src/jvm/clojure/lang/PersistentVector.java | 6 +--
test/clojure/test_clojure/sequences.clj | 50 +++++++++++++++++++++-
4 files changed, 63 insertions(+), 14 deletions(-)
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index caa1fb387e..688f5dd7fb 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -2942,7 +2942,11 @@
(rf result input))))))))
([n coll]
(if (instance? clojure.lang.IDrop coll)
- (or (.drop ^clojure.lang.IDrop coll n) ())
+ (or
+ (if (pos? n)
+ (.drop ^clojure.lang.IDrop coll (if (int? n) n (Math/ceil n)))
+ (seq coll))
+ ())
(let [step (fn [n coll]
(let [s (seq coll)]
(if (and (pos? n) s)
@@ -3170,7 +3174,9 @@
:static true}
[coll n]
(if (instance? clojure.lang.IDrop coll)
- (.drop ^clojure.lang.IDrop coll n)
+ (if (pos? n)
+ (.drop ^clojure.lang.IDrop coll (if (int? n) n (Math/ceil n)))
+ (seq coll))
(loop [n n xs (seq coll)]
(if (and xs (pos? n))
(recur (dec n) (next xs))
@@ -3182,7 +3188,9 @@
:static true}
[coll n]
(if (instance? clojure.lang.IDrop coll)
- (or (.drop ^clojure.lang.IDrop coll n) ())
+ (if (pos? n)
+ (or (.drop ^clojure.lang.IDrop coll (if (int? n) n (Math/ceil n))) ())
+ coll)
(loop [n n xs coll]
(if-let [xs (and (pos? n) (seq xs))]
(recur (dec n) (rest xs))
diff --git a/src/jvm/clojure/lang/IDrop.java b/src/jvm/clojure/lang/IDrop.java
index 336c0a8270..fb9c9f746f 100644
--- a/src/jvm/clojure/lang/IDrop.java
+++ b/src/jvm/clojure/lang/IDrop.java
@@ -20,11 +20,8 @@ public interface IDrop{
* useful if the returned coll implements IDrop for subsequent use in a
* partition-like scenario.
*
- * If n is <= 0, return this.
- * If n drops to or past the end of the collection, return null.
- *
- * @param n Items to drop
- * @return Collection that is Sequential, ISeq, and IReduceInit
+ * @param n Items to drop, must be > 0
+ * @return Collection that is Sequential, ISeq, and IReduceInit, or null if past the end
*/
Sequential drop(int n);
}
diff --git a/src/jvm/clojure/lang/PersistentVector.java b/src/jvm/clojure/lang/PersistentVector.java
index 973cb2a31a..a68e5c6a62 100644
--- a/src/jvm/clojure/lang/PersistentVector.java
+++ b/src/jvm/clojure/lang/PersistentVector.java
@@ -366,9 +366,7 @@ public Object kvreduce(IFn f, Object init){
}
public Sequential drop(int n) {
- if(n < 0) {
- return this;
- } else if(n < cnt) {
+ if(n < cnt) {
int offset = n%32;
return new ChunkedSeq(this, this.arrayFor(n), n-offset, offset);
} else {
@@ -617,7 +615,7 @@ public int count(){
ensureEditable();
return cnt;
}
-
+
Node ensureEditable(Node node){
if(node.edit == root.edit)
return node;
diff --git a/test/clojure/test_clojure/sequences.clj b/test/clojure/test_clojure/sequences.clj
index bc79c1f62e..0ce1122bf5 100644
--- a/test/clojure/test_clojure/sequences.clj
+++ b/test/clojure/test_clojure/sequences.clj
@@ -855,7 +855,9 @@
(take 0 [1 2 3 4 5]) ()
(take -1 [1 2 3 4 5]) ()
- (take -2 [1 2 3 4 5]) () ))
+ (take -2 [1 2 3 4 5]) ()
+
+ (take 1/4 [1 2 3 4 5]) '(1) ))
(deftest test-drop
@@ -867,8 +869,52 @@
(drop 0 [1 2 3 4 5]) '(1 2 3 4 5)
(drop -1 [1 2 3 4 5]) '(1 2 3 4 5)
- (drop -2 [1 2 3 4 5]) '(1 2 3 4 5) ))
+ (drop -2 [1 2 3 4 5]) '(1 2 3 4 5)
+
+ (drop 1/4 [1 2 3 4 5]) '(2 3 4 5) )
+
+ (are [coll] (= (drop 4 coll) (drop -2 (drop 4 coll)))
+ [0 1 2 3 4 5]
+ (seq [0 1 2 3 4 5])
+ (range 6)
+ (repeat 6 :x))
+ )
+
+(deftest test-nthrest
+ (are [x y] (= x y)
+ (nthrest [1 2 3 4 5] 1) '(2 3 4 5)
+ (nthrest [1 2 3 4 5] 3) '(4 5)
+ (nthrest [1 2 3 4 5] 5) ()
+ (nthrest [1 2 3 4 5] 9) ()
+
+ (nthrest [1 2 3 4 5] 0) '(1 2 3 4 5)
+ (nthrest [1 2 3 4 5] -1) '(1 2 3 4 5)
+ (nthrest [1 2 3 4 5] -2) '(1 2 3 4 5)
+
+ (nthrest [1 2 3 4 5] 1/4) '(2 3 4 5)
+ (nthrest [1 2 3 4 5] 1.2) '(3 4 5) )
+
+ ;; (nthrest coll 0) should return coll
+ (are [coll] (let [r (nthrest coll 0)] (and (= coll r) (= (class coll) (class r))))
+ [1 2 3]
+ (seq [1 2 3])
+ (range 10)
+ (repeat 10 :x)
+ (seq "abc") ))
+
+(deftest test-nthnext
+ (are [x y] (= x y)
+ (nthnext [1 2 3 4 5] 1) '(2 3 4 5)
+ (nthnext [1 2 3 4 5] 3) '(4 5)
+ (nthnext [1 2 3 4 5] 5) nil
+ (nthnext [1 2 3 4 5] 9) nil
+
+ (nthnext [1 2 3 4 5] 0) '(1 2 3 4 5)
+ (nthnext [1 2 3 4 5] -1) '(1 2 3 4 5)
+ (nthnext [1 2 3 4 5] -2) '(1 2 3 4 5)
+ (nthnext [1 2 3 4 5] 1/4) '(2 3 4 5)
+ (nthnext [1 2 3 4 5] 1.2) '(3 4 5) ))
(deftest test-take-nth
(are [x y] (= x y)
From 307eb04df551f9bdf7d155799ad922f8d9a531d1 Mon Sep 17 00:00:00 2001
From: Frank Yin
Date: Mon, 5 Jun 2023 11:53:28 -0500
Subject: [PATCH 146/285] CLJ-2686: Fixes ConcurrentModificationException by
iterating through Java properties in a thread-safe manner
---
src/clj/clojure/core/server.clj | 17 ++++++++++-------
test/clojure/test_clojure/server.clj | 18 ++++++++++++++++++
2 files changed, 28 insertions(+), 7 deletions(-)
diff --git a/src/clj/clojure/core/server.clj b/src/clj/clojure/core/server.clj
index 3cda4d1f74..05a5a20999 100644
--- a/src/clj/clojure/core/server.clj
+++ b/src/clj/clojure/core/server.clj
@@ -16,6 +16,7 @@
[clojure.lang LineNumberingPushbackReader]
[java.net InetAddress Socket ServerSocket SocketException]
[java.io Reader Writer PrintWriter BufferedWriter BufferedReader InputStreamReader OutputStreamWriter]
+ [java.util Properties]
[java.util.concurrent.locks ReentrantLock]))
(set! *warn-on-reflection* true)
@@ -145,14 +146,16 @@
(defn- parse-props
"Parse clojure.server.* from properties to produce a map of server configs."
- [props]
+ [^Properties props]
(reduce
- (fn [acc [^String k ^String v]]
- (let [[k1 k2 k3] (str/split k #"\.")]
- (if (and (= k1 "clojure") (= k2 "server"))
- (conj acc (merge {:name k3} (edn/read-string v)))
- acc)))
- [] props))
+ (fn [acc ^String k]
+ (let [[k1 k2 k3] (str/split k #"\.")]
+ (if (and (= k1 "clojure") (= k2 "server"))
+ (let [v (get props k)]
+ (conj acc (merge {:name k3} (edn/read-string v))))
+ acc)))
+ []
+ (.stringPropertyNames props)))
(defn start-servers
"Start all servers specified in the system properties."
diff --git a/test/clojure/test_clojure/server.clj b/test/clojure/test_clojure/server.clj
index 1d24d0b2dd..4d4f20a480 100644
--- a/test/clojure/test_clojure/server.clj
+++ b/test/clojure/test_clojure/server.clj
@@ -9,6 +9,7 @@
; Author: Alex Miller
(ns clojure.test-clojure.server
+ (:import java.util.Random)
(:require [clojure.test :refer :all])
(:require [clojure.core.server :as s]))
@@ -21,9 +22,26 @@
(is (= (ex-data e) opts))
(is (= msg (.getMessage e))))))
+(defn create-random-thread
+ []
+ (Thread.
+ (fn []
+ (let [random (new Random)]
+ (while (not (.isInterrupted (Thread/currentThread)))
+ (System/setProperty (Integer/toString (.nextInt random)) (Integer/toString (.nextInt random))))))))
+
(deftest test-validate-opts
(check-invalid-opts {} "Missing required socket server property :name")
(check-invalid-opts {:name "a" :accept 'clojure.core/+} "Missing required socket server property :port")
(doseq [port [-1 "5" 999999]]
(check-invalid-opts {:name "a" :port port :accept 'clojure.core/+} (str "Invalid socket server port: " port)))
(check-invalid-opts {:name "a" :port 5555} "Missing required socket server property :accept"))
+
+(deftest test-parse-props
+ (let [thread (create-random-thread)]
+ (.start thread)
+ (Thread/sleep 1000)
+ (try
+ (is (>= (count
+ (#'s/parse-props (System/getProperties))) 0))
+ (finally (.interrupt thread)))))
From 61778b8761d4a60e48ff6ebcd238002081715ac2 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Tue, 1 Feb 2022 20:20:03 -0600
Subject: [PATCH 147/285] CLJ-2694 Fix ratio cases due to use of Long/MIN_VALUE
to defer to bigint impl
---
src/jvm/clojure/lang/Numbers.java | 4 ++++
test/clojure/test_clojure/numbers.clj | 12 +++++++++++-
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/src/jvm/clojure/lang/Numbers.java b/src/jvm/clojure/lang/Numbers.java
index 82d8e6914c..661c051d36 100644
--- a/src/jvm/clojure/lang/Numbers.java
+++ b/src/jvm/clojure/lang/Numbers.java
@@ -530,6 +530,10 @@ static long gcd(long u, long v){
public Number divide(Number x, Number y){
long n = x.longValue();
long val = y.longValue();
+
+ if(n == Long.MIN_VALUE || val == Long.MIN_VALUE)
+ return BIGINT_OPS.divide(x, y);
+
long gcd = gcd(n, val);
if(gcd == 0)
return num(0);
diff --git a/test/clojure/test_clojure/numbers.clj b/test/clojure/test_clojure/numbers.clj
index bf822cc947..be4f7a0a60 100644
--- a/test/clojure/test_clojure/numbers.clj
+++ b/test/clojure/test_clojure/numbers.clj
@@ -598,7 +598,17 @@ Math/pow overflows to Infinity."
(is (== (denominator 1/2) 2))
(is (== (numerator 1/2) 1))
(is (= (bigint (/ 100000000000000000000 3)) 33333333333333333333))
- (is (= (long 10000000000000000000/3) 3333333333333333333)))
+ (is (= (long 10000000000000000000/3) 3333333333333333333))
+
+ ;; special cases around Long/MIN_VALUE
+ (is (= (/ 1 Long/MIN_VALUE) -1/9223372036854775808))
+ (is (true? (< (/ 1 Long/MIN_VALUE) 0)))
+ (is (true? (< (* 1 (/ 1 Long/MIN_VALUE)) 0)))
+ (is (= (abs (/ 1 Long/MIN_VALUE)) 1/9223372036854775808))
+ (is (false? (< (abs (/ 1 Long/MIN_VALUE)) 0)))
+ (is (false? (< (* 1 (abs (/ 1 Long/MIN_VALUE))) 0)))
+ (is (= (/ Long/MIN_VALUE -3) 9223372036854775808/3))
+ (is (false? (< (/ Long/MIN_VALUE -3) 0))))
(deftest test-arbitrary-precision-subtract
(are [x y] (= x y)
From 595e6dd1674304ace56c14b19bc816410bf6687c Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Tue, 11 Apr 2023 14:42:44 -0500
Subject: [PATCH 148/285] CLJ-2741 Revert change in metadata on string sequence
drop
---
src/jvm/clojure/lang/StringSeq.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/jvm/clojure/lang/StringSeq.java b/src/jvm/clojure/lang/StringSeq.java
index 3163380898..dbad70810e 100644
--- a/src/jvm/clojure/lang/StringSeq.java
+++ b/src/jvm/clojure/lang/StringSeq.java
@@ -61,7 +61,7 @@ public int count(){
public Sequential drop(int n) {
int ii = i + n;
if (ii < s.length()) {
- return new StringSeq(_meta, s, ii);
+ return new StringSeq(null, s, ii);
} else {
return null;
}
From e10105134e30917c369d92476f95dbb58906ee29 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 11 May 2023 16:30:29 -0500
Subject: [PATCH 149/285] CLJ-2773 Fix param names in
c.j.process/[to,from]-file to match docstring
---
src/clj/clojure/java/process.clj | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/clj/clojure/java/process.clj b/src/clj/clojure/java/process.clj
index 881b69413b..b8d2e6cba4 100644
--- a/src/clj/clojure/java/process.clj
+++ b/src/clj/clojure/java/process.clj
@@ -41,18 +41,18 @@
"Coerce f to a file per clojure.java.io/file and return a ProcessBuilder.Redirect writing to the file.
Set ':append' in opts to append. This can be passed to 'start' in :out or :err."
{:added "1.12"}
- ^ProcessBuilder$Redirect [file & {:keys [append] :as opts}]
- (let [f (jio/file file)]
+ ^ProcessBuilder$Redirect [f & {:keys [append] :as opts}]
+ (let [fo (jio/file f)]
(if append
- (ProcessBuilder$Redirect/appendTo f)
- (ProcessBuilder$Redirect/to f))))
+ (ProcessBuilder$Redirect/appendTo fo)
+ (ProcessBuilder$Redirect/to fo))))
(defn from-file
"Coerce f to a file per clojure.java.io/file and return a ProcessBuilder.Redirect reading from the file.
This can be passed to 'start' in :in."
{:added "1.12"}
- ^ProcessBuilder$Redirect [file]
- (ProcessBuilder$Redirect/from (jio/file file)))
+ ^ProcessBuilder$Redirect [f]
+ (ProcessBuilder$Redirect/from (jio/file f)))
(defn start
"Starts an external command as args and optional leading opts map:
From 3ba6473ca3f44015fa425f6b1c135dcec583dadc Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Wed, 17 May 2023 11:20:49 -0500
Subject: [PATCH 150/285] CLJ-2776 Fix redirect err stream to out stream in
c.j.process
---
src/clj/clojure/java/process.clj | 4 ++--
test/clojure/test_clojure/java/process.clj | 25 ++++++++++++++++++++++
2 files changed, 27 insertions(+), 2 deletions(-)
create mode 100644 test/clojure/test_clojure/java/process.clj
diff --git a/src/clj/clojure/java/process.clj b/src/clj/clojure/java/process.clj
index b8d2e6cba4..1b375c176f 100644
--- a/src/clj/clojure/java/process.clj
+++ b/src/clj/clojure/java/process.clj
@@ -86,7 +86,7 @@
(when in (.redirectInput pb ^ProcessBuilder$Redirect (to-redirect in)))
(when out (.redirectOutput pb ^ProcessBuilder$Redirect (to-redirect out)))
(cond
- (= err :stdout) (.redirectErrorStream pb)
+ (= err :stdout) (.redirectErrorStream pb true)
err (.redirectError pb ^ProcessBuilder$Redirect (to-redirect err)))
(when env
(let [pb-env (.environment pb)]
@@ -134,7 +134,7 @@
(let [[opts command] (if (map? (first opts+args))
[(first opts+args) (rest opts+args)]
[{} opts+args])
- opts (merge opts {:err :inherit})]
+ opts (merge {:err :inherit} opts)]
(let [state (apply start opts command)
out-promise (promise)
capture-fn #(deliver out-promise (capture (:out state)))]
diff --git a/test/clojure/test_clojure/java/process.clj b/test/clojure/test_clojure/java/process.clj
new file mode 100644
index 0000000000..170e801cd1
--- /dev/null
+++ b/test/clojure/test_clojure/java/process.clj
@@ -0,0 +1,25 @@
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html at the root of this distribution.
+; By using this software in any fashion, you are agreeing to be bound by
+; the terms of this license.
+; You must not remove this notice, or any other, from this software.
+
+(ns clojure.test-clojure.java.process
+ (:require
+ [clojure.test :refer :all]
+ [clojure.java.process :as p]
+ [clojure.string :as str]))
+
+(deftest test-stderr-redirect
+ ;; capture to stdout and return string
+ (is (not (str/blank? (p/exec "bash" "-c" "ls"))))
+
+ ;; print to stderr, capture nil
+ (is (nil? (p/exec "bash" "-c" "ls >&2")))
+
+ ;; redirect, then capture to string
+ (is (not (str/blank? (p/exec {:err :stdout} "bash" "-c" "ls >&2")))))
+
+
From 23be02537c6413064d4e55f3c8842846d627938b Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Wed, 17 May 2023 11:31:00 -0500
Subject: [PATCH 151/285] CLJ-2778 Fix docstring options for
c.j.process/capture
---
src/clj/clojure/java/process.clj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/clj/clojure/java/process.clj b/src/clj/clojure/java/process.clj
index 1b375c176f..3b8f41f11e 100644
--- a/src/clj/clojure/java/process.clj
+++ b/src/clj/clojure/java/process.clj
@@ -116,7 +116,7 @@
(defn capture
"Read from input-stream until EOF and return a String (or nil if 0 length).
- Takes same opts as clojure.java.io/copy - :buffer and :encoding"
+ Takes same opts as clojure.java.io/copy - :buffer-size and :encoding"
{:added "1.12"}
[input-stream & opts]
(let [writer (StringWriter.)]
From ef3cd497dc881021a80cad3a9d44925dcb3a0f4e Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Fri, 19 May 2023 12:50:05 -0500
Subject: [PATCH 152/285] CLJ-2779 c.j.process/start - remove unnecessary nil
arg handling
---
src/clj/clojure/java/process.clj | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/clj/clojure/java/process.clj b/src/clj/clojure/java/process.clj
index 3b8f41f11e..8dce0a6a63 100644
--- a/src/clj/clojure/java/process.clj
+++ b/src/clj/clojure/java/process.clj
@@ -82,12 +82,12 @@
:discard (ProcessBuilder$Redirect/to @null-file)
;; in Java 9+, just use ProcessBuilder$Redirect/DISCARD
x))]
- (.directory pb (jio/file (or dir ".")))
- (when in (.redirectInput pb ^ProcessBuilder$Redirect (to-redirect in)))
- (when out (.redirectOutput pb ^ProcessBuilder$Redirect (to-redirect out)))
- (cond
+ (.directory pb (jio/file dir))
+ (.redirectInput pb ^ProcessBuilder$Redirect (to-redirect in))
+ (.redirectOutput pb ^ProcessBuilder$Redirect (to-redirect out))
+ (if
(= err :stdout) (.redirectErrorStream pb true)
- err (.redirectError pb ^ProcessBuilder$Redirect (to-redirect err)))
+ (.redirectError pb ^ProcessBuilder$Redirect (to-redirect err)))
(when env
(let [pb-env (.environment pb)]
(run! (fn [[k v]] (.put pb-env k v)) env)))
From 5fd97af9a4370bbf2075a53d9c3bf83decf289ce Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Mon, 12 Jun 2023 20:46:53 -0500
Subject: [PATCH 153/285] update test workflow to support workflow dispatch,
not fail fast, and to add oracle distribution
---
.github/workflows/test.yml | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 6d8bb247e3..6bb91f2a39 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -1,15 +1,21 @@
name: Test
-on: [push]
+on:
+ push:
+ workflow_dispatch:
jobs:
test:
strategy:
+ fail-fast: false
matrix:
os: [ubuntu-latest] # macOS-latest, windows-latest]
java-version: ["8", "11", "17"]
distribution: ["temurin", "corretto"]
profile: ["test-direct", "test-no-direct"]
+ include:
+ - distribution: "oracle"
+ java-version: "17"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
From 1b3a5065b7f533d4fd7109686fcc4489163b916c Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Mon, 12 Jun 2023 21:14:36 -0500
Subject: [PATCH 154/285] test workflow - remove extra distribution
---
.github/workflows/test.yml | 3 ---
1 file changed, 3 deletions(-)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 6bb91f2a39..0197cfdd91 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -13,9 +13,6 @@ jobs:
java-version: ["8", "11", "17"]
distribution: ["temurin", "corretto"]
profile: ["test-direct", "test-no-direct"]
- include:
- - distribution: "oracle"
- java-version: "17"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
From b81660ee9cc50d830c33f943dbda8606ebac3806 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Fri, 19 May 2023 15:48:27 -0500
Subject: [PATCH 155/285] CLJ-2767 Fix initialization and setting of *repl*
---
src/clj/clojure/core.clj | 2 +-
src/clj/clojure/main.clj | 38 ++++++++++++-------------
test/clojure/test_clojure/repl/deps.clj | 30 +++++++++++++++++++
3 files changed, 50 insertions(+), 20 deletions(-)
create mode 100644 test/clojure/test_clojure/repl/deps.clj
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 688f5dd7fb..b8d9ff9f54 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -6351,7 +6351,7 @@ fails, attempts to require sym's namespace and retries."
(def ^:dynamic
^{:doc "Bound to true in a repl thread"
:added "1.12"}
- *repl*)
+ *repl* false)
(defn trampoline
"trampoline can be used to convert algorithms requiring mutual
diff --git a/src/clj/clojure/main.clj b/src/clj/clojure/main.clj
index bb72673107..89baed7f81 100644
--- a/src/clj/clojure/main.clj
+++ b/src/clj/clojure/main.clj
@@ -93,7 +93,6 @@
*unchecked-math* *unchecked-math*
*assert* *assert*
clojure.spec.alpha/*explain-out* clojure.spec.alpha/*explain-out*
- *repl* true
*1 nil
*2 nil
*3 nil
@@ -447,24 +446,25 @@ by default when a new command-line REPL is started."} repl-requires
(caught e)
(set! *e e))))]
(with-bindings
- (try
- (init)
- (catch Throwable e
- (caught e)
- (set! *e e)))
- (prompt)
- (flush)
- (loop []
- (when-not
- (try (identical? (read-eval-print) request-exit)
- (catch Throwable e
- (caught e)
- (set! *e e)
- nil))
- (when (need-prompt)
- (prompt)
- (flush))
- (recur))))))
+ (binding [*repl* true]
+ (try
+ (init)
+ (catch Throwable e
+ (caught e)
+ (set! *e e)))
+ (prompt)
+ (flush)
+ (loop []
+ (when-not
+ (try (identical? (read-eval-print) request-exit)
+ (catch Throwable e
+ (caught e)
+ (set! *e e)
+ nil))
+ (when (need-prompt)
+ (prompt)
+ (flush))
+ (recur)))))))
(defn load-script
"Loads Clojure source from a file or resource given its path. Paths
diff --git a/test/clojure/test_clojure/repl/deps.clj b/test/clojure/test_clojure/repl/deps.clj
new file mode 100644
index 0000000000..010d041fea
--- /dev/null
+++ b/test/clojure/test_clojure/repl/deps.clj
@@ -0,0 +1,30 @@
+(ns clojure.test-clojure.repl.deps
+ (:use clojure.test)
+ (:require [clojure.string :as str]
+ [clojure.repl.deps :as deps]
+ [clojure.main :as main]))
+
+(defmacro with-dynamic-loader
+ "Ensure or install a DynamicClassLoader as the current thread's
+ context classloader and execute the body."
+ [& body]
+ `(let [t# (Thread/currentThread)
+ cl# (.getContextClassLoader t#)]
+ (if (instance? ~'clojure.lang.DynamicClassLoader cl#)
+ (do ~@body)
+ (try
+ (.setContextClassLoader t# (clojure.lang.DynamicClassLoader. cl#))
+ ~@body
+ (finally
+ (.setContextClassLoader t# cl#))))))
+
+(deftest test-no-add-libs-outside-repl
+ (try
+ (deps/add-lib 'org.clojure/data.json {:mvn/version "2.4.0"})
+ (is false "add-libs outside repl should throw")
+ (catch Throwable t (str/includes? (ex-message t) "add-libs")))
+
+ (with-dynamic-loader
+ (binding [*repl* true]
+ (is (some #{'org.clojure/data.json} (deps/add-lib 'org.clojure/data.json {:mvn/version "2.4.0"})))))
+ )
From 5d81bb40dc3c48340c461b0d85c7363831140e24 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 15 Jun 2023 13:06:17 -0500
Subject: [PATCH 156/285] CLJ-2769 Pass invoke-tool args over stdin rather than
in command
---
src/clj/clojure/java/process.clj | 43 +++++++++++++++++++++++---
src/clj/clojure/tools/deps/interop.clj | 29 ++++++++++-------
2 files changed, 56 insertions(+), 16 deletions(-)
diff --git a/src/clj/clojure/java/process.clj b/src/clj/clojure/java/process.clj
index 8dce0a6a63..74f87af7b6 100644
--- a/src/clj/clojure/java/process.clj
+++ b/src/clj/clojure/java/process.clj
@@ -25,7 +25,8 @@
[java.io StringWriter File]
[java.lang ProcessBuilder ProcessBuilder$Redirect Process]
[java.util List]
- [clojure.lang IDeref IBlockingDeref]))
+ [clojure.lang IDeref IBlockingDeref]
+ [java.util.concurrent Executors ExecutorService ThreadFactory]))
(set! *warn-on-reflection* true)
@@ -125,6 +126,40 @@
(when-not (zero? (.length s))
s))))
+;; A thread factory for daemon threads
+(defonce ^:private io-thread-factory
+ (let [counter (atom 0)]
+ (reify ThreadFactory
+ (newThread [_ r]
+ (doto (Thread. r)
+ (.setName (str "Clojure Process IO " (swap! counter inc)))
+ (.setDaemon true))))))
+
+;; An ExecutorService for cached, daemon threads
+(defonce ^:private io-executor
+ (Executors/newCachedThreadPool ^ThreadFactory io-thread-factory))
+
+(defn io-task
+ {:skip-wiki true}
+ [^Runnable f]
+ (let [f (bound-fn* f)
+ fut (.submit ^ExecutorService io-executor ^Callable f)]
+ (reify
+ clojure.lang.IDeref
+ (deref [_] (#'clojure.core/deref-future fut))
+ clojure.lang.IBlockingDeref
+ (deref
+ [_ timeout-ms timeout-val]
+ (#'clojure.core/deref-future fut timeout-ms timeout-val))
+ clojure.lang.IPending
+ (isRealized [_] (.isDone fut))
+ java.util.concurrent.Future
+ (get [_] (.get fut))
+ (get [_ timeout unit] (.get fut timeout unit))
+ (isCancelled [_] (.isCancelled fut))
+ (isDone [_] (.isDone fut))
+ (cancel [_ interrupt?] (.cancel fut interrupt?)))))
+
(defn exec
"Execute a command and on successful exit, return the captured output,
else throw RuntimeException. Args are the same as 'start' and options
@@ -136,11 +171,9 @@
[{} opts+args])
opts (merge {:err :inherit} opts)]
(let [state (apply start opts command)
- out-promise (promise)
- capture-fn #(deliver out-promise (capture (:out state)))]
- (doto (Thread. ^Runnable capture-fn) (.setDaemon true) (.start))
+ captured (io-task #(capture (:out state)))]
(if (ok? state)
- @out-promise
+ @captured
(throw (RuntimeException. (str "Process failed with exit=" (.exitValue ^Process (:process state)))))))))
(comment
diff --git a/src/clj/clojure/tools/deps/interop.clj b/src/clj/clojure/tools/deps/interop.clj
index 54449b731a..3d0bdf1d7a 100644
--- a/src/clj/clojure/tools/deps/interop.clj
+++ b/src/clj/clojure/tools/deps/interop.clj
@@ -9,7 +9,8 @@
"Functions for invoking Java processes and invoking tools via the Clojure CLI."
(:require
[clojure.java.process :as proc]
- [clojure.edn :as edn]))
+ [clojure.edn :as edn]
+ [clojure.java.io :as jio]))
(defn ^:dynamic invoke-tool
"Invoke tool using Clojure CLI. Args (one of :tool-alias or :tool-name, and :fn
@@ -28,18 +29,24 @@
:as opts}]
(when-not (or tool-name tool-alias) (throw (ex-info "Either :tool-alias or :tool-name must be provided" (or opts {}))))
(when-not (symbol? fn) (throw (ex-info (str "fn should be a symbol " fn) (or opts {}))))
- (let [args (assoc args :clojure.exec/invoke :fn)
+ (let [args (conj [fn] (assoc args :clojure.exec/invoke :fn))
_ (when (:debug opts) (println "args" args))
- command-strs [command (str "-T" (or tool-alias tool-name)) (pr-str fn) (pr-str args)]
+ command-strs [command (str "-T" (or tool-alias tool-name)) "-"]
_ (when (:debug opts) (apply println "Invoking: " command-strs))
- envelope (edn/read-string (apply proc/exec command-strs))]
- (if preserve-envelope
- envelope
- (let [{:keys [tag val]} envelope
- parsed-val (edn/read-string val)]
- (if (= :ret tag)
- parsed-val
- (throw (ex-info (:cause parsed-val) (or parsed-val {}))))))))
+ {:keys [in out]} (apply proc/start command-strs)]
+ (proc/io-task
+ #(with-open [w (jio/writer in)]
+ (doseq [a args]
+ (.write w (pr-str a))
+ (.write w " "))))
+ (let [envelope (edn/read-string (proc/capture out))]
+ (if preserve-envelope
+ envelope
+ (let [{:keys [tag val]} envelope
+ parsed-val (edn/read-string val)]
+ (if (= :ret tag)
+ parsed-val
+ (throw (ex-info (:cause parsed-val) (or parsed-val {})))))))))
(comment
;; regular invocation, should return {:hi :there}
From 1e317674fce4c60a2fba0b7147bc7fa3240a3fe6 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Wed, 28 Jun 2023 14:09:33 -0500
Subject: [PATCH 157/285] Test workflow - add Clojure CLI
---
.github/workflows/test.yml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 0197cfdd91..12e4d74ef3 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -22,5 +22,9 @@ jobs:
java-version: ${{ matrix.java-version }}
distribution: ${{ matrix.distribution }}
cache: 'maven'
+ - name: Set up Clojure CLI
+ uses: DeLaGuardo/setup-clojure@11.0
+ with:
+ cli: latest
- name: Build with Maven
run: mvn -ntp -B -P${{ matrix.profile }} clean test
From a0c75edfd13bf274309b500e5179cc4af70ddc09 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Wed, 28 Jun 2023 15:55:42 -0500
Subject: [PATCH 158/285] Revert "Test workflow - add Clojure CLI"
This reverts commit 1e317674fce4c60a2fba0b7147bc7fa3240a3fe6.
---
.github/workflows/test.yml | 4 ----
1 file changed, 4 deletions(-)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 12e4d74ef3..0197cfdd91 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -22,9 +22,5 @@ jobs:
java-version: ${{ matrix.java-version }}
distribution: ${{ matrix.distribution }}
cache: 'maven'
- - name: Set up Clojure CLI
- uses: DeLaGuardo/setup-clojure@11.0
- with:
- cli: latest
- name: Build with Maven
run: mvn -ntp -B -P${{ matrix.profile }} clean test
From 451428121695564de8390053a04a500c068cd668 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Wed, 28 Jun 2023 15:56:22 -0500
Subject: [PATCH 159/285] Remove add-lib test
---
test/clojure/test_clojure/repl/deps.clj | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/test/clojure/test_clojure/repl/deps.clj b/test/clojure/test_clojure/repl/deps.clj
index 010d041fea..a2a69078d1 100644
--- a/test/clojure/test_clojure/repl/deps.clj
+++ b/test/clojure/test_clojure/repl/deps.clj
@@ -18,13 +18,13 @@
(finally
(.setContextClassLoader t# cl#))))))
-(deftest test-no-add-libs-outside-repl
- (try
- (deps/add-lib 'org.clojure/data.json {:mvn/version "2.4.0"})
- (is false "add-libs outside repl should throw")
- (catch Throwable t (str/includes? (ex-message t) "add-libs")))
-
- (with-dynamic-loader
- (binding [*repl* true]
- (is (some #{'org.clojure/data.json} (deps/add-lib 'org.clojure/data.json {:mvn/version "2.4.0"})))))
- )
+;(deftest test-no-add-libs-outside-repl
+; (try
+; (deps/add-lib 'org.clojure/data.json {:mvn/version "2.4.0"})
+; (is false "add-libs outside repl should throw")
+; (catch Throwable t (str/includes? (ex-message t) "add-libs")))
+;
+; (with-dynamic-loader
+; (binding [*repl* true]
+; (is (some #{'org.clojure/data.json} (deps/add-lib 'org.clojure/data.json {:mvn/version "2.4.0"})))))
+; )
From 155511362a9fed75b6f18058c1bbc455ab81eb47 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Wed, 28 Jun 2023 16:55:25 -0500
Subject: [PATCH 160/285] Fix bad character in Javadoc
---
src/jvm/clojure/lang/IDrop.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/jvm/clojure/lang/IDrop.java b/src/jvm/clojure/lang/IDrop.java
index fb9c9f746f..153ab0858f 100644
--- a/src/jvm/clojure/lang/IDrop.java
+++ b/src/jvm/clojure/lang/IDrop.java
@@ -20,7 +20,7 @@ public interface IDrop{
* useful if the returned coll implements IDrop for subsequent use in a
* partition-like scenario.
*
- * @param n Items to drop, must be > 0
+ * @param n Items to drop, must be > 0
* @return Collection that is Sequential, ISeq, and IReduceInit, or null if past the end
*/
Sequential drop(int n);
From 5bc453ec87116e2732a3dd31f05ba519563a2bfc Mon Sep 17 00:00:00 2001
From: clojure-build
Date: Wed, 28 Jun 2023 22:03:20 +0000
Subject: [PATCH 161/285] [maven-release-plugin] prepare release
clojure-1.12.0-alpha4
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 2dd37faaad..f91f362548 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.12.0-master-SNAPSHOT
+ 1.12.0-alpha4
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- HEAD
+ clojure-1.12.0-alpha4
From 501348deb1e49736f3c49f2c177bcff947fe3a17 Mon Sep 17 00:00:00 2001
From: clojure-build
Date: Wed, 28 Jun 2023 22:03:20 +0000
Subject: [PATCH 162/285] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index f91f362548..2dd37faaad 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.12.0-alpha4
+ 1.12.0-master-SNAPSHOT
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- clojure-1.12.0-alpha4
+ HEAD
From 2a058814e5fa3e8fb630ae507c3fa7dc865138c6 Mon Sep 17 00:00:00 2001
From: JarrodCTaylor
Date: Mon, 31 Jul 2023 16:15:44 -0500
Subject: [PATCH 163/285] Add github action to build api docs
---
.github/workflows/doc-build.yml | 72 +++++++++++++++++++++++++++++++++
1 file changed, 72 insertions(+)
create mode 100644 .github/workflows/doc-build.yml
diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml
new file mode 100644
index 0000000000..819cba1268
--- /dev/null
+++ b/.github/workflows/doc-build.yml
@@ -0,0 +1,72 @@
+name: Build API Docs
+
+on:
+ workflow_dispatch:
+ inputs:
+ commit:
+ description: 'Commit changes to gh-pages branch'
+ type: boolean
+ required: true
+ default: false
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+
+ - name: Set up Java
+ uses: actions/setup-java@v3
+ with:
+ java-version: 8
+ distribution: 'temurin'
+
+ - name: Set up Clojure
+ uses: DeLaGuardo/setup-clojure@11.0
+ with:
+ cli: 'latest'
+
+ - name: Cache clojure dependencies
+ uses: actions/cache@v3
+ with:
+ path: |
+ ~/.m2/repository
+ ~/.gitlibs
+ key: cljdeps-${{ hashFiles('deps.edn') }}
+ restore-keys: cljdeps-
+
+ - name: Clone clojure api doc repo
+ uses: actions/checkout@v3
+ with:
+ repository: clojure/clojure-api-doc
+ path: clojure-api-doc
+ fetch-depth: 0
+
+ - name: Clone clojure source code repo into clojure-api-doc
+ uses: actions/checkout@v3
+ with:
+ path: clojure-api-doc/repo
+ fetch-depth: 0
+
+ - name: Clone clojure gh-pages branch into clojure-api-doc
+ uses: actions/checkout@v3
+ with:
+ repository: clojure/clojure
+ path: clojure-api-doc/repo-docs
+ ref: 'gh-pages'
+ fetch-depth: 0
+
+ - name: Install markdown
+ run: sudo apt install markdown
+
+ - name: Call clojure-api-doc build.sh
+ run: bash ${GITHUB_WORKSPACE}/clojure-api-doc/build.sh
+
+ - name: Commit
+ if: ${{inputs.commit}}
+ run: |
+ git config --global user.name clojure-build
+ git config --global user.email "clojure-build@users.noreply.github.com"
+ cd clojure-api-doc/repo-docs
+ git add -u -v
+ git commit -m "Autodoc commit"
+ git push origin gh-pages
From 6975553804b0f8da9e196e6fb97838ea4e153564 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Tue, 29 Aug 2023 17:18:30 -0500
Subject: [PATCH 164/285] update links in readme - fix feedback and license
links
---
readme.txt | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/readme.txt b/readme.txt
index 815cf9126d..f91653f8b5 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,14 +1,14 @@
* Clojure
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
- * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+ * Eclipse Public License 1.0 (https://opensource.org/license/epl-1-0/)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
Docs: https://clojure.org
-Feedback: http://groups.google.com/group/clojure
+Feedback: https://ask.clojure.org
Getting Started: https://clojure.org/guides/getting_started
To build and run locally with Ant:
@@ -76,7 +76,7 @@ under the Apache License:
Apache License
Version 2.0, January 2004
- http://www.apache.org/licenses/
+ https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
From 496f60ab6b90a535afef562fed4b9af753bc8e15 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Fri, 20 Oct 2023 15:51:45 -0500
Subject: [PATCH 165/285] CLJ-2804 - to avoid i/o blocking under synchronized,
replace with ReentrantLock in LazySeq and Delay
Signed-off-by: Alex Miller
---
src/jvm/clojure/lang/Delay.java | 64 ++++++++++--------
src/jvm/clojure/lang/LazySeq.java | 104 +++++++++++++++++++++++-------
2 files changed, 115 insertions(+), 53 deletions(-)
diff --git a/src/jvm/clojure/lang/Delay.java b/src/jvm/clojure/lang/Delay.java
index ffd418962d..f0aa855d20 100644
--- a/src/jvm/clojure/lang/Delay.java
+++ b/src/jvm/clojure/lang/Delay.java
@@ -12,15 +12,20 @@
package clojure.lang;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
public class Delay implements IDeref, IPending{
-volatile Object val;
-volatile Throwable exception;
-volatile IFn fn;
-
-public Delay(IFn fn){
- this.fn = fn;
- this.val = null;
- this.exception = null;
+Object val;
+Throwable exception;
+IFn fn;
+volatile Lock lock;
+
+public Delay(IFn f){
+ fn = f;
+ val = null;
+ exception = null;
+ lock = new ReentrantLock();
}
static public Object force(Object x) {
@@ -29,32 +34,35 @@ static public Object force(Object x) {
: x;
}
-public Object deref() {
- if(fn != null)
- {
- synchronized(this)
- {
- //double check
- if(fn!=null)
- {
- try
- {
- val = fn.invoke();
- }
- catch(Throwable t)
- {
- exception = t;
- }
- fn = null;
- }
- }
+private void realize() {
+ Lock l = lock;
+ if(l != null) {
+ l.lock();
+ try {
+ if(fn!=null) {
+ try {
+ val = fn.invoke();
+ } catch (Throwable t) {
+ exception = t;
+ }
+ fn = null;
+ lock = null;
+ }
+ } finally {
+ l.unlock();
}
+ }
+}
+
+public Object deref() {
+ if(lock != null)
+ realize();
if(exception != null)
throw Util.sneakyThrow(exception);
return val;
}
public boolean isRealized(){
- return fn == null;
+ return lock == null;
}
}
diff --git a/src/jvm/clojure/lang/LazySeq.java b/src/jvm/clojure/lang/LazySeq.java
index f9f9bfb24e..5c243c1ebb 100644
--- a/src/jvm/clojure/lang/LazySeq.java
+++ b/src/jvm/clojure/lang/LazySeq.java
@@ -12,24 +12,30 @@
package clojure.lang;
+import java.io.IOException;
+import java.io.ObjectInputStream;
import java.util.*;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
public final class LazySeq extends Obj implements ISeq, Sequential, List, IPending, IHashEq{
-private static final long serialVersionUID = 7700080124382322592L;
+private static final long serialVersionUID = -7345643944998411680L;
private IFn fn;
private Object sv;
private ISeq s;
+private Lock lock;
-public LazySeq(IFn fn){
- this.fn = fn;
+public LazySeq(IFn f){
+ fn = f;
+ lock = new ReentrantLock();
}
-private LazySeq(IPersistentMap meta, ISeq s){
+private LazySeq(IPersistentMap meta, ISeq seq){
super(meta);
- this.fn = null;
- this.s = s;
+ fn = null;
+ s = seq;
}
public Obj withMeta(IPersistentMap meta){
@@ -38,29 +44,66 @@ public Obj withMeta(IPersistentMap meta){
return new LazySeq(meta, seq());
}
-final synchronized Object sval(){
- if(fn != null)
- {
- sv = fn.invoke();
- fn = null;
+// MUST be locked when called!
+final private void force() {
+ if (fn != null) {
+ sv = fn.invoke();
+ fn = null;
+ }
+}
+
+final private void lockAndForce() {
+ Lock l = lock;
+ if(l != null) {
+ l.lock();
+ try {
+ force();
+ } finally {
+ l.unlock();
}
+ }
+}
+
+final private Object sval() {
+ if(fn != null)
+ lockAndForce();
if(sv != null)
return sv;
return s;
}
-final synchronized public ISeq seq(){
- sval();
- if(sv != null)
- {
- Object ls = sv;
- sv = null;
- while(ls instanceof LazySeq)
- {
- ls = ((LazySeq)ls).sval();
- }
- s = RT.seq(ls);
+final private Object unwrap(Object ls){
+ while(ls instanceof LazySeq) {
+ ls = ((LazySeq) ls).sval();
+ }
+ return ls;
+}
+
+final private void realize() {
+ Lock l = lock;
+ if(l != null) {
+ l.lock();
+ try {
+ //must re-examine under lock
+ if(lock != null) {
+ force();
+ Object ls = sv;
+ sv = null;
+ if(ls instanceof LazySeq)
+ ls = unwrap(ls);
+ s = RT.seq(ls);
+ lock = null;
+ }
+ }
+ finally {
+ l.unlock();
}
+ }
+}
+
+public final ISeq seq(){
+ if(lock != null)
+ realize();
return s;
}
@@ -243,8 +286,19 @@ public boolean addAll(int index, Collection c){
throw new UnsupportedOperationException();
}
-
-synchronized public boolean isRealized(){
- return fn == null;
+public boolean isRealized(){
+ if(lock != null) {
+ Lock l = lock;
+ if(l != null) {
+ l.lock();
+ try {
+ return lock == null;
+ } finally {
+ l.unlock();
+ }
+ }
+ }
+ return true;
}
}
+
From c6122ebe26ba25a33f44190dc8994614d4cf13f4 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Fri, 20 Oct 2023 16:23:09 -0500
Subject: [PATCH 166/285] Add Java 21 to test matrix
---
.github/workflows/test.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 0197cfdd91..b50a9813d1 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -10,7 +10,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest] # macOS-latest, windows-latest]
- java-version: ["8", "11", "17"]
+ java-version: ["8", "11", "17", "21"]
distribution: ["temurin", "corretto"]
profile: ["test-direct", "test-no-direct"]
runs-on: ${{ matrix.os }}
From 5dbf142e0d734e617154f143cd02e3078955ea86 Mon Sep 17 00:00:00 2001
From: clojure-build
Date: Fri, 20 Oct 2023 21:34:37 +0000
Subject: [PATCH 167/285] [maven-release-plugin] prepare release
clojure-1.12.0-alpha5
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 2dd37faaad..d790670455 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.12.0-master-SNAPSHOT
+ 1.12.0-alpha5
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- HEAD
+ clojure-1.12.0-alpha5
From 08a2d9bdd013143a87e50fa82e9740e2ab4ee2c2 Mon Sep 17 00:00:00 2001
From: clojure-build
Date: Fri, 20 Oct 2023 21:34:37 +0000
Subject: [PATCH 168/285] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index d790670455..2dd37faaad 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.12.0-alpha5
+ 1.12.0-master-SNAPSHOT
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- clojure-1.12.0-alpha5
+ HEAD
From e0efe77eb39f3868ba02c5951f036a48262e80ff Mon Sep 17 00:00:00 2001
From: fogus
Date: Thu, 8 Feb 2024 13:10:12 -0500
Subject: [PATCH 169/285] CLJ-2806: Add support for a param-tags metadata via
the special metadata field :param-tags taking a vector of symbols
corresponding to types and a new reader for supporting it of ^[T U]. Also,
added support for using param-tags at compile time to specify a single method
for invocation or acccess of qualified class members Class/member and
constructors via Class/new. Also implemented method values by expanding the
meaning of qualified class member symbolic representation. Added compiler
macro to identify Class/method and Class/new member symbols and emit a
single-arity function with type hints and coercions on args to fully resolve
target constructor, instance method, or static method call. There is no way
to specify bound methods.
---
src/clj/clojure/gvec.clj | 4 +-
src/jvm/clojure/lang/Compiler.java | 349 +++++++++++++++++++-
src/jvm/clojure/lang/LispReader.java | 4 +-
src/jvm/clojure/lang/RT.java | 1 +
test/clojure/test_clojure/java_interop.clj | 2 +-
test/clojure/test_clojure/method_thunks.clj | 53 +++
test/clojure/test_clojure/param_tags.clj | 52 +++
7 files changed, 450 insertions(+), 15 deletions(-)
create mode 100644 test/clojure/test_clojure/method_thunks.clj
create mode 100644 test/clojure/test_clojure/param_tags.clj
diff --git a/src/clj/clojure/gvec.clj b/src/clj/clojure/gvec.clj
index 52075c420e..590cfcecf9 100644
--- a/src/clj/clojure/gvec.clj
+++ b/src/clj/clojure/gvec.clj
@@ -87,7 +87,7 @@
(.chunkedNext this)))
(more [this]
(let [s (.next this)]
- (or s (clojure.lang.PersistentList/EMPTY))))
+ (or s clojure.lang.PersistentList/EMPTY)))
(cons [this o]
(clojure.lang.Cons. o this))
(count [this]
@@ -124,7 +124,7 @@
(new VecSeq am vec (.arrayFor vec nexti) nexti 0 nil))))
(chunkedMore [this]
(let [s (.chunkedNext this)]
- (or s (clojure.lang.PersistentList/EMPTY))))
+ (or s clojure.lang.PersistentList/EMPTY)))
clojure.lang.IMeta
(meta [_]
diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java
index 758108e8bc..4bb066c92d 100644
--- a/src/jvm/clojure/lang/Compiler.java
+++ b/src/jvm/clojure/lang/Compiler.java
@@ -15,15 +15,18 @@
//*
import clojure.asm.*;
+import clojure.asm.Type;
import clojure.asm.commons.GeneratorAdapter;
import clojure.asm.commons.Method;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
+import java.lang.reflect.Executable;
import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
+import java.util.stream.Collectors;
//*/
/*
@@ -1113,6 +1116,264 @@ static Class tagToClass(Object tag) {
}
}
+// In invocation position
+// direct invocation of resolved constructor, static, or instance method
+// OR legacy static method invocation with inference
+// this is the ONLY valid case where method is unresolved by end of constructor
+// In value position, will emit as a method thunk (error if not resolved)
+static class MethodValueExpr implements Expr {
+ private final Class c;
+ private final List hintedSig;
+ private final Executable method;
+ private final Symbol methodSymbol;
+ private final String methodName;
+
+ public MethodValueExpr(Class methodClass, Symbol sym){
+ c = methodClass;
+ methodSymbol = sym;
+ methodName = sym.name;
+
+ List methods = methodsWithName(c, methodName);
+ if(methods.isEmpty())
+ throw noMethodWithNameException(c, methodName);
+
+ hintedSig = tagsToClasses(paramTagsOf(sym));
+ if(hintedSig != null) {
+ method = resolveHintedMethod(c, methodName, hintedSig, methods);
+ } else {
+ Executable maybeMethod = null;
+ if(methods.size() == 1) {
+ // Only 1 method - no inference needed
+ maybeMethod = methods.get(0);
+ }
+ else if(methods.size() > 1) {
+ // Only statics supported at this point (needs inference)
+
+ if(methodNamesConstructor(c, methodName))
+ throw overloadNeedsParamTagsException(c, methodName);
+
+ // Filter out instance methods (no inference allowed)
+ List staticMethods = methods.stream()
+ .filter(m -> Modifier.isStatic(m.getModifiers()))
+ .collect(Collectors.toList());
+ if(staticMethods.size() == 1)
+ maybeMethod = staticMethods.get(0);
+ else if(staticMethods.isEmpty())
+ throw overloadNeedsParamTagsException(c, methodName);
+ // else not resolved, static method w/inference
+ }
+ method = maybeMethod;
+ }
+ }
+
+ private static Executable resolveHintedMethod(Class c, String methodName, List hintedSig, List methods) {
+ final int arity = hintedSig.size();
+ List filteredMethods = methods.stream()
+ .filter(m -> m.getParameterCount() == arity)
+ .filter(m -> !m.isSynthetic()) // remove bridge/lambda methods
+ .filter(m -> signatureMatches(hintedSig, m))
+ .collect(Collectors.toList());
+
+ if(filteredMethods.size() == 1)
+ return filteredMethods.get(0);
+ else
+ throw paramTagsDontResolveException(c, methodName, hintedSig, filteredMethods.size());
+ }
+
+ private static boolean methodNamesConstructor(Class c, String methodName) {
+ return c != null && methodName.equals("new");
+ }
+
+ private static List methodsWithName(Class c, String name) {
+ final Executable[] methods;
+ final String methodName;
+ if (methodNamesConstructor(c, name)) {
+ methods = c.getConstructors();
+ methodName = c.getName();
+ }
+ else {
+ methods = c.getMethods();
+ methodName = name;
+ }
+
+ return Arrays.stream(methods)
+ .filter(m -> m.getName().equals(methodName))
+ .collect(Collectors.toList());
+ }
+
+ public boolean isResolved() {
+ return method != null;
+ }
+
+ @Override
+ public Object eval() {
+ return toFnExpr(this).eval();
+ }
+
+ @Override
+ public void emit(C context, ObjExpr objx, GeneratorAdapter gen) {
+ toFnExpr(this).emit(context, objx, gen);
+ }
+
+ @Override
+ public boolean hasJavaClass() {
+ return true;
+ }
+
+ @Override
+ public Class getJavaClass() {
+ return AFn.class;
+ }
+
+ private static FnExpr toFnExpr(MethodValueExpr mexpr) {
+ // If not resolved by this point we were not given param-tags
+ // and named method was overloaded.
+ if(!mexpr.isResolved()) {
+ throw overloadNeedsParamTagsException(mexpr.c, mexpr.methodName);
+ }
+
+ // return hint symbol
+ Symbol retTag = null;
+ Class retClass = mexpr.method instanceof Constructor ? mexpr.c
+ : ((java.lang.reflect.Method)mexpr.method).getReturnType();
+ if (isHintablePrimitive(retClass) || !retClass.isPrimitive()) {
+ retTag = primTag(retClass);
+ retTag = (retTag != null) ? retTag : Symbol.intern(null, retClass.getName());
+ }
+
+ return buildThunk(mexpr.c, mexpr.method, mexpr.methodSymbol,
+ isInstanceMethod(mexpr.method) ? THIS : null, retTag);
+ }
+
+ private static boolean isHintablePrimitive(Class c) {
+ return Long.TYPE.equals(c) || Double.TYPE.equals(c);
+ }
+
+ private static Symbol primTag(Class c) {
+ String t = null;
+ if(c.isPrimitive()) {
+ t = c.getName();
+ }
+ else if(c.isArray() && c.getComponentType().isPrimitive()) {
+ t = c.getName() + "s";
+ }
+ return (t == null) ? null : Symbol.intern(null, t);
+ }
+
+ // All buildThunk methods currently return a new FnExpr on every call
+ // TBD: caching/reuse of thunks
+ private static FnExpr buildThunk(Class c, Executable method, Symbol methodSymbol, Symbol instanceParam, Symbol retHint) {
+ // (fn invoke__Class_meth (^retHint? [this? primHintedArgs*] (methodSymbol this? primHintedArgs*)))
+ IPersistentVector params = PersistentVector.EMPTY;
+ if(instanceParam != null) params = params.cons(instanceParam);
+ // hinted params
+ Class[] paramTypes = method.getParameterTypes();
+ for(int i = 0; i hintedSig) {
+ return PersistentVector.create(hintedSig.stream()
+ .map(tag -> tag == null ? PARAM_TAG_ANY : tag)
+ .collect(Collectors.toList()));
+ }
+
+ static IllegalArgumentException noMethodWithNameException(Class c, String methodName) {
+ return new IllegalArgumentException("Could not find "
+ + methodDescription(c, methodName));
+ }
+
+ static IllegalArgumentException overloadNeedsParamTagsException(Class c, String methodName) {
+ return new IllegalArgumentException("Multiple matches for "
+ + methodDescription(c, methodName)
+ + ", use param-tags to specify");
+ }
+
+ static IllegalArgumentException paramTagsDontResolveException(Class c, String methodName, List hintedSig, int found) {
+ return new IllegalArgumentException("Expected to find 1 matching signature for "
+ + methodDescription(c, methodName)
+ + " but found " + found
+ + " with param-tags " + toParamTags(hintedSig));
+ }
+}
+
+final static Symbol PARAM_TAG_ANY = Symbol.intern(null, "_");
+
+private static IPersistentVector paramTagsOf(Symbol sym){
+ Object paramTags = RT.get(RT.meta(sym), RT.PARAM_TAGS_KEY);
+
+ if(paramTags != null && !(paramTags instanceof IPersistentVector))
+ throw new IllegalArgumentException("param-tags of symbol "
+ + sym + " should be a vector.");
+
+ return (IPersistentVector) paramTags;
+}
+
+// calls tagToClass on every element, unless it encounters _ which becomes null
+private static List tagsToClasses(IPersistentVector paramTags) {
+ if(paramTags == null) return null;
+
+ List sig = new ArrayList<>();
+ for (ISeq s = RT.seq(paramTags); s!=null; s = s.next()) {
+ Object t = s.first();
+ if (t.equals(PARAM_TAG_ANY))
+ sig.add(null);
+ else
+ sig.add(HostExpr.tagToClass(t));
+ }
+ return sig;
+}
+
+private static boolean signatureMatches(List sig, Executable method)
+{
+ Class[] methodSig = method.getParameterTypes();
+ if(methodSig.length != sig.size()) return false;
+
+ for (int i = 0; i < methodSig.length; i++)
+ if (sig.get(i) != null && !sig.get(i).equals(methodSig[i]))
+ return false;
+
+ return true;
+};
+
+static boolean isStaticMethod(Executable method) {
+ return method instanceof java.lang.reflect.Method && Modifier.isStatic(method.getModifiers());
+}
+
+static boolean isInstanceMethod(Executable method) {
+ return method instanceof java.lang.reflect.Method && !Modifier.isStatic(method.getModifiers());
+}
+
+static boolean isConstructor(Executable method) {
+ return method instanceof Constructor;
+}
+
+private static void checkMethodArity(Executable method, int argCount) {
+ if(method.getParameterCount() != argCount)
+ throw new IllegalArgumentException("Invocation of "
+ + MethodValueExpr.methodDescription(method.getDeclaringClass(), method.getName())
+ + " expected " + method.getParameterCount() + " arguments, but received " + argCount);
+}
+
static abstract class FieldExpr extends HostExpr{
}
@@ -1456,6 +1717,21 @@ static class InstanceMethodExpr extends MethodExpr{
final static Method invokeInstanceMethodMethod =
Method.getMethod("Object invokeInstanceMethod(Object,String,Object[])");
+ public InstanceMethodExpr(String source, int line, int column, Symbol tag, Expr target,
+ String methodName, java.lang.reflect.Method preferredMethod, IPersistentVector args, boolean tailPosition)
+ {
+ checkMethodArity(preferredMethod, RT.count(args));
+
+ this.source = source;
+ this.line = line;
+ this.column = column;
+ this.args = args;
+ this.methodName = methodName;
+ this.target = target;
+ this.tag = tag;
+ this.tailPosition = tailPosition;
+ this.method = preferredMethod;
+ }
public InstanceMethodExpr(String source, int line, int column, Symbol tag, Expr target,
String methodName, IPersistentVector args, boolean tailPosition)
@@ -1652,6 +1928,29 @@ static class StaticMethodExpr extends MethodExpr{
final static Keyword warnOnBoxedKeyword = Keyword.intern("warn-on-boxed");
Class jc;
+ public StaticMethodExpr(String source, int line, int column, Symbol tag, Class c,
+ String methodName, java.lang.reflect.Method preferredMethod, IPersistentVector args, boolean tailPosition)
+ {
+ checkMethodArity(preferredMethod, RT.count(args));
+
+ this.c = c;
+ this.methodName = methodName;
+ this.args = args;
+ this.source = source;
+ this.line = line;
+ this.column = column;
+ this.tag = tag;
+ this.tailPosition = tailPosition;
+ this.method = preferredMethod;
+
+ if(method != null && warnOnBoxedKeyword.equals(RT.UNCHECKED_MATH.deref()) && isBoxedMath(method))
+ {
+ RT.errPrintWriter()
+ .format("Boxed math warning, %s:%d:%d - call: %s.\n",
+ SOURCE_PATH.deref(), line, column, method.toString());
+ }
+ }
+
public StaticMethodExpr(String source, int line, int column, Symbol tag, Class c,
String methodName, IPersistentVector args, boolean tailPosition)
{
@@ -2563,6 +2862,13 @@ public static class NewExpr implements Expr{
Method.getMethod("Object invokeConstructor(Class,Object[])");
final static Method forNameMethod = Method.getMethod("Class classForName(String)");
+ public NewExpr(Class c, Constructor preferredConstructor, IPersistentVector args, int line, int column) {
+ checkMethodArity(preferredConstructor, RT.count(args));
+
+ this.args = args;
+ this.c = c;
+ this.ctor = preferredConstructor;
+ }
public NewExpr(Class c, IPersistentVector args, int line, int column) {
this.args = args;
@@ -3877,17 +4183,46 @@ static public Expr parse(C context, ISeq form) {
return new KeywordInvokeExpr((String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form),
(KeywordExpr) fexpr, target);
}
+
+ // Preserving the existing static field bug that replaces a reference in parens with
+ // the field itself rather than trying to invoke the value in the field. This is
+ // an exception to the uniform Class/member qualification per CLJ-2806 ticket.
+ if(fexpr instanceof StaticFieldExpr)
+ return fexpr;
+
PersistentVector args = PersistentVector.EMPTY;
for(ISeq s = RT.seq(form.next()); s != null; s = s.next())
{
args = args.cons(analyze(context, s.first()));
}
+
+ if(fexpr instanceof MethodValueExpr)
+ return toHostExpr((MethodValueExpr)fexpr, (String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form), tailPosition, args);
+
// if(args.count() > MAX_POSITIONAL_ARITY)
// throw new IllegalArgumentException(
// String.format("No more than %d args supported", MAX_POSITIONAL_ARITY));
return new InvokeExpr((String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form), fexpr, args, tailPosition);
}
+
+ private static Expr toHostExpr(MethodValueExpr mexpr, String source, int line, int column, Symbol tag, boolean tailPosition, IPersistentVector args) {
+ if(!mexpr.isResolved()) {
+ // default to static method with inference
+ return new StaticMethodExpr(source, line, column, tag, mexpr.c, munge(mexpr.methodName), args, tailPosition);
+ }
+
+ if(isConstructor(mexpr.method))
+ return new NewExpr(mexpr.c, (Constructor) mexpr.method, args, line, column);
+
+ if(isInstanceMethod(mexpr.method))
+ return new InstanceMethodExpr(source, line, column, tag, (Expr) RT.first(args),
+ munge(mexpr.methodName), (java.lang.reflect.Method) mexpr.method, PersistentVector.create(RT.next(args)),
+ tailPosition);
+
+ return new StaticMethodExpr(source, line, column, tag, mexpr.c,
+ munge(mexpr.methodName), (java.lang.reflect.Method) mexpr.method, args, tailPosition);
+ }
}
static class SourceDebugExtensionAttribute extends Attribute{
@@ -7039,16 +7374,6 @@ public static Object macroexpand1(Object x) {
}
return preserveTag(form, RT.listStar(DOT, target, meth, form.next().next()));
}
- else if(namesStaticMember(sym))
- {
- Symbol target = Symbol.intern(sym.ns);
- Class c = HostExpr.maybeClass(target, false);
- if(c != null)
- {
- Symbol meth = Symbol.intern(sym.name);
- return preserveTag(form, RT.listStar(DOT, target, meth, form.next()));
- }
- }
else
{
//(s.substring 2 5) => (. s substring 2 5)
@@ -7308,8 +7633,10 @@ private static Expr analyzeSymbol(Symbol sym) {
{
if(Reflector.getField(c, sym.name, true) != null)
return new StaticFieldExpr(lineDeref(), columnDeref(), c, sym.name, tag);
- throw Util.runtimeException("Unable to find static field: " + sym.name + " in " + c);
+ else
+ return new MethodValueExpr(c, sym);
}
+
}
}
//Var v = lookupVar(sym, false);
diff --git a/src/jvm/clojure/lang/LispReader.java b/src/jvm/clojure/lang/LispReader.java
index 9fcb7b5aa9..104b05d1a2 100644
--- a/src/jvm/clojure/lang/LispReader.java
+++ b/src/jvm/clojure/lang/LispReader.java
@@ -956,8 +956,10 @@ public Object invoke(Object reader, Object caret, Object opts, Object pendingFor
meta = RT.map(RT.TAG_KEY, meta);
else if (meta instanceof Keyword)
meta = RT.map(meta, RT.T);
+ else if (meta instanceof IPersistentVector)
+ meta = RT.map(RT.PARAM_TAGS_KEY, meta);
else if(!(meta instanceof IPersistentMap))
- throw new IllegalArgumentException("Metadata must be Symbol,Keyword,String or Map");
+ throw new IllegalArgumentException("Metadata must be Symbol,Keyword,String,Vector or Map");
Object o = read(r, true, null, true, opts, pendingForms);
if(o instanceof IMeta)
diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java
index 5d20ef4964..86c89d69fb 100644
--- a/src/jvm/clojure/lang/RT.java
+++ b/src/jvm/clojure/lang/RT.java
@@ -189,6 +189,7 @@ else if(s.equals("false"))
Var.intern(CLOJURE_NS, Symbol.intern("*err*"),
new PrintWriter(new OutputStreamWriter(System.err), true)).setDynamic();
final static Keyword TAG_KEY = Keyword.intern(null, "tag");
+final static Keyword PARAM_TAGS_KEY = Keyword.intern(null, "param-tags");
final static Keyword CONST_KEY = Keyword.intern(null, "const");
final static public Var AGENT = Var.intern(CLOJURE_NS, Symbol.intern("*agent*"), null).setDynamic();
static Object readeval = readTrueFalseUnknown(System.getProperty("clojure.read.eval","true"));
diff --git a/test/clojure/test_clojure/java_interop.clj b/test/clojure/test_clojure/java_interop.clj
index 4925284cc2..da7075d138 100644
--- a/test/clojure/test_clojure/java_interop.clj
+++ b/test/clojure/test_clojure/java_interop.clj
@@ -408,7 +408,7 @@
(to-array [1 2 3]) ))
(defn queue [& contents]
- (apply conj (clojure.lang.PersistentQueue/EMPTY) contents))
+ (apply conj clojure.lang.PersistentQueue/EMPTY contents))
(defn array-typed-equals [expected actual]
(and (= (class expected) (class actual))
diff --git a/test/clojure/test_clojure/method_thunks.clj b/test/clojure/test_clojure/method_thunks.clj
new file mode 100644
index 0000000000..06ac5a3f6e
--- /dev/null
+++ b/test/clojure/test_clojure/method_thunks.clj
@@ -0,0 +1,53 @@
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html at the root of this distribution.
+; By using this software in any fashion, you are agreeing to be bound by
+; the terms of this license.
+; You must not remove this notice, or any other, from this software.
+; Authors: Fogus
+
+(ns clojure.test-clojure.method-thunks
+ (:use clojure.test)
+ (:import (clojure.lang Compiler Tuple)
+ (java.util Arrays UUID Locale)
+ clojure.lang.IFn$LL))
+
+(set! *warn-on-reflection* true)
+
+(deftest method-arity-selection
+ (is (= '([] [] [])
+ (take 3 (repeatedly ^[] Tuple/create))))
+ (is (= '([1] [2] [3])
+ (map ^[_] Tuple/create [1 2 3])))
+ (is (= '([1 4] [2 5] [3 6])
+ (map ^[_ _] Tuple/create [1 2 3] [4 5 6])))
+ (is (thrown? Exception (eval 'clojure.lang.Tuple/create))))
+
+(deftest method-signature-selection
+ (is (= [1.23 3.14]
+ (map ^[double] Math/abs [1.23 -3.14])))
+ (is (= [(float 1.23) (float 3.14)]
+ (map ^[float] Math/abs [1.23 -3.14])))
+ (is (= [1 2 3]
+ (map ^[long] Math/abs [1 2 -3])))
+ (is (= [#uuid "00000000-0000-0001-0000-000000000002"]
+ (map ^[long long] UUID/new [1] [2])))
+ (is (= '("a" "12")
+ (map ^[Object] String/valueOf ["a" 12])))
+ (is (= ["A" "B" "C"]
+ (map ^[java.util.Locale] String/toUpperCase ["a" "b" "c"] (repeat java.util.Locale/ENGLISH))))
+ (is (thrown? ClassCastException
+ (doall (map ^[long] String/valueOf [12 "a"])))))
+
+(def mt ^[_] Tuple/create)
+(def mts {:fromString ^[_] UUID/fromString})
+
+(deftest method-thunks-in-structs
+ (is (= #uuid "00000000-0000-0001-0000-000000000002"
+ ((:fromString mts) "00000000-0000-0001-0000-000000000002")))
+ (is (= [1] (mt 1))))
+
+(deftest primitive-hinting
+ (is (instance? clojure.lang.IFn$DO ^[double] String/valueOf))
+ (is (instance? clojure.lang.IFn$LL ^[long] Math/abs)))
diff --git a/test/clojure/test_clojure/param_tags.clj b/test/clojure/test_clojure/param_tags.clj
new file mode 100644
index 0000000000..2b6f1bffe2
--- /dev/null
+++ b/test/clojure/test_clojure/param_tags.clj
@@ -0,0 +1,52 @@
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html at the root of this distribution.
+; By using this software in any fashion, you are agreeing to be bound by
+; the terms of this license.
+; You must not remove this notice, or any other, from this software.
+; Authors: Fogus
+
+(ns clojure.test-clojure.param-tags
+ (:use clojure.test)
+ (:require [clojure.test-helper :refer [should-not-reflect]])
+ (:import (clojure.lang Tuple)
+ (java.util Arrays UUID Locale)))
+
+(set! *warn-on-reflection* true)
+
+(deftest no-hints-with-param-tags
+ (should-not-reflect
+ (defn touc-no-reflect [s]
+ (^[] String/toUpperCase s)))
+ (should-not-reflect
+ (defn touc-no-reflectq [s]
+ (^[] java.lang.String/toUpperCase s)))
+ (should-not-reflect
+ (defn touc-no-reflect-arg-tags [s]
+ (^[java.util.Locale] String/toUpperCase s java.util.Locale/ENGLISH))))
+
+(deftest param-tags-in-invocation-positions
+ (testing "qualified static method invocation"
+ (is (= 3 (^[long] Math/abs -3)))
+ (is (= [1 2] (^[_ _] Tuple/create 1 2)))
+ (is (= "42" (Long/toString 42))))
+ (testing "qualified ctor invocation"
+ (is (= (^[long long] UUID/new 1 2) #uuid "00000000-0000-0001-0000-000000000002"))
+ (is (= (^[long long] java.util.UUID/new 1 2) #uuid "00000000-0000-0001-0000-000000000002"))
+ (is (= "a" (^[String] String/new "a"))))
+ (testing "qualified instance method invocation"
+ (is (= \A (String/charAt "A" 0)))
+ (is (= "A" (^[java.util.Locale] String/toUpperCase "a" java.util.Locale/ENGLISH)))
+ (is (= "A" (^[Locale] String/toUpperCase "a" java.util.Locale/ENGLISH)))
+ (is (= 65 (aget (^[String] String/getBytes "A" "US-ASCII") 0)))
+ (is (= "42" (^[] Long/toString 42))))
+ (testing "string repr array type resolutions"
+ (let [lary (long-array [1 2 3 4 99 100])
+ oary (into-array [1 2 3 4 99 100])
+ sary (into-array String ["a" "b" "c"])]
+ (is (= 4 (^[longs long] Arrays/binarySearch lary (long 99))))
+ (is (= 4 (^[objects _] Arrays/binarySearch oary 99)))
+ (is (= 4 (^["[Ljava.lang.Object;" _] Arrays/binarySearch oary 99)))
+ (is (= 1 (^["[Ljava.lang.Object;" _] Arrays/binarySearch sary "b"))))))
+
From a6937fd02a9f0833dd4b2a352fd481b577584cae Mon Sep 17 00:00:00 2001
From: JarrodCTaylor
Date: Sat, 3 Feb 2024 10:29:56 -0600
Subject: [PATCH 170/285] Expanded param-tags tests
---
test/clojure/test_clojure/param_tags.clj | 138 ++++++++++++++++++-
test/java/clojure/test/ConcreteClass.java | 8 ++
test/java/clojure/test/GenericInterface.java | 5 +
test/java/clojure/test/SwissArmy.java | 39 ++++++
4 files changed, 187 insertions(+), 3 deletions(-)
create mode 100644 test/java/clojure/test/ConcreteClass.java
create mode 100644 test/java/clojure/test/GenericInterface.java
create mode 100644 test/java/clojure/test/SwissArmy.java
diff --git a/test/clojure/test_clojure/param_tags.clj b/test/clojure/test_clojure/param_tags.clj
index 2b6f1bffe2..c4c57d60e1 100644
--- a/test/clojure/test_clojure/param_tags.clj
+++ b/test/clojure/test_clojure/param_tags.clj
@@ -9,9 +9,14 @@
(ns clojure.test-clojure.param-tags
(:use clojure.test)
- (:require [clojure.test-helper :refer [should-not-reflect]])
- (:import (clojure.lang Tuple)
- (java.util Arrays UUID Locale)))
+ (:require
+ [clojure.string :as str]
+ [clojure.reflect :as r]
+ [clojure.test-helper :refer [should-not-reflect]])
+ (:import
+ (clojure.test SwissArmy ConcreteClass)
+ (clojure.lang Tuple Compiler Compiler$CompilerException)
+ (java.util Arrays UUID Locale)))
(set! *warn-on-reflection* true)
@@ -50,3 +55,130 @@
(is (= 4 (^["[Ljava.lang.Object;" _] Arrays/binarySearch oary 99)))
(is (= 1 (^["[Ljava.lang.Object;" _] Arrays/binarySearch sary "b"))))))
+
+;; Mapping of symbols returned from reflect call to :parameter-type used as arguments to .getDeclaredMethod,
+;; :arg-type used as arguments to the methods and constructors being tested, :arg-tag used as arg-tags
+;; to the methods and constructors being tested.
+(def reflected-parameter-types {'int {:parameter-type Integer/TYPE
+ :arg-type "(int 42)"
+ :arg-tag "int"}
+ 'boolean {:parameter-type Boolean/TYPE
+ :arg-type "true"
+ :arg-tag "boolean"}
+ 'long {:parameter-type Long/TYPE
+ :arg-type "42"
+ :arg-tag "long"}
+ 'long<> {:parameter-type (Class/forName "[J")
+ :arg-type "(long-array [1 2])"
+ :arg-tag "long*"}
+ 'int<><> {:parameter-type (Class/forName "[[I")
+ :arg-type "(make-array Integer/TYPE 1 2)"
+ :arg-tag "int**"}
+ 'java.lang.Object<> {:parameter-type (Class/forName "[Ljava.lang.Object;")
+ :arg-type "(into-array [1 2])"
+ :arg-tag "\"[Ljava.lang.Object;\""}
+ 'java.lang.String<> {:parameter-type (Class/forName "[Ljava.lang.String;")
+ :arg-type "(into-array [\"a\" \"b\"])"
+ :arg-tag "\"[Ljava.lang.String;\""}})
+
+(defn is-static-method? [class method-name params]
+ (let [method (.getDeclaredMethod ^Class class ^String (name method-name) ^"[Ljava.lang.Object;" params)]
+ (java.lang.reflect.Modifier/isStatic (.getModifiers method))))
+
+(defn get-methods
+ "Reflect the class located at `path`, filter out the public members, add a :type
+ of :constructor, :static, or :instance to each."
+ [path]
+ (let [reflected-class (r/reflect (resolve path))
+ public (filter #(contains? (:flags %) :public) (:members reflected-class))]
+ (reduce (fn [res m]
+ (let [class (-> m :declaring-class resolve)
+ params (into-array Class (map #(-> % reflected-parameter-types :parameter-type) (:parameter-types m)))]
+ (cond
+ (not (contains? m :return-type)) (conj res (assoc m :type :constructor))
+ (is-static-method? class (:name m) params) (conj res (assoc m :type :static))
+ :else (conj res (assoc m :type :instance)))))
+ [] public)))
+
+(defn exercise-constructor
+ "Provided a map of data returned from a call to reflect representing a constructor.
+ Construct a new instance of the class providing the appropriate arg-tags and return
+ a map containing the new instance and expected target class"
+ [{:keys [declaring-class parameter-types] :as m}]
+ (let [target-class (-> declaring-class str Class/forName)
+ args (str/join " " (map #(-> % reflected-parameter-types :arg-type) parameter-types))
+ arg-tags (str/join " " (map #(-> % reflected-parameter-types :arg-tag) parameter-types))
+ fun-call-str (read-string (str "(^[" arg-tags "] " declaring-class ". " args ")"))
+ _ (should-not-reflect #(eval 'fun-call-str))
+ new-instance (eval fun-call-str)]
+ {:expected target-class :actual new-instance}))
+
+(defn exercise-static-method
+ "Provided a map of data returned from a call to reflect representing a static class method.
+ Call the static method providing the appropriate arg-tags and return a map containing
+ the actual and expected response."
+ [{:keys [name declaring-class parameter-types]}]
+ (let [class (str declaring-class)
+ method (str name)
+ args (str/join " " (map #(-> % reflected-parameter-types :arg-type) parameter-types))
+ arg-tags (str/join " " (map #(-> % reflected-parameter-types :arg-tag) parameter-types))
+ expected-response (str/join "-" parameter-types)
+ fun-call-str (read-string (str "(^[" arg-tags "] " class "/" method " " args ")"))
+ _ (should-not-reflect #(eval 'fun-call-str))
+ response (eval fun-call-str)]
+ {:expected expected-response :actual response}))
+
+(defn exercise-instance-method
+ "Provided a map of data returned from a call to reflect representing a class instance method.
+ Call the method providing the appropriate arg-tags and return a map containing
+ the actual and expected response."
+ [{:keys [name declaring-class parameter-types]}]
+ (let [method (str name)
+ args (str/join " " (map #(-> % reflected-parameter-types :arg-type) parameter-types))
+ arg-tags (str/join " " (map #(-> % reflected-parameter-types :arg-tag) parameter-types))
+ expected-response (str/join "-" parameter-types)
+ fun-call-str (read-string (str "(^[" arg-tags "] " declaring-class "/" method " " "(" declaring-class ".)" " " args ")"))
+ _ (should-not-reflect #(eval 'fun-call-str))
+ response (eval fun-call-str)]
+ {:expected expected-response :actual response}))
+
+(deftest arg-tags-in-constructors-and-static-and-instance-methods
+ (doseq [m (get-methods 'clojure.test.SwissArmy)]
+ (case (:type m)
+ :constructor (let [{:keys [expected actual]} (exercise-constructor m)]
+ (is (instance? expected actual)))
+ :static (let [{:keys [expected actual]} (exercise-static-method m)]
+ (is (= expected actual)))
+ :instance (let [{:keys [expected actual]} (exercise-instance-method m)]
+ (is (= expected actual))))))
+
+(defmacro arg-tags-called-in-macro
+ [a-type b-type a b]
+ `(^[~a-type ~b-type] SwissArmy/staticArityOverloadMethod ~a ~b))
+
+(deftest arg-tags-in-macro
+ (is (= "int-int" (arg-tags-called-in-macro int int 1 2))))
+
+(deftest bridge-methods
+ (testing "Allows correct intended usage."
+ (let [concrete (ConcreteClass.)]
+ (is (= 42 (^[Integer] ConcreteClass/stampWidgets concrete (int 99))))))
+ (testing "Will not call bridge method."
+ (is (thrown? Compiler$CompilerException
+ (eval '(let [concrete (clojure.test.ConcreteClass.)]
+ (^[Object] ConcreteClass/stampWidgets concrete (int 99))))))))
+
+
+(deftest incorrect-arity-invocation-error-messages
+
+ (testing "Invocation with param-tags having incorrect number of args"
+ (let [e (try
+ (eval '(^[long] Math/abs -1 -2 -3))
+ (catch Compiler$CompilerException e (str "-> " (.getMessage (.getCause e)))))]
+ (is (not (nil? (re-find #"expected 1.*received 3" e))) "Error message was expected to indicate 1 argument was expected but 2 were provided")))
+
+ (testing "Invocation without param-tags having incorrect number of args"
+ (let [e (try
+ (eval '(java.util.UUID/fromString "a" 1))
+ (catch Compiler$CompilerException e (str "-> " (.getMessage (.getCause e)))))]
+ (is (not (nil? (re-find #"expected 1.*received 2" e))) "Error message was expected to indicate 1 argument was expected but 2 were provided"))))
\ No newline at end of file
diff --git a/test/java/clojure/test/ConcreteClass.java b/test/java/clojure/test/ConcreteClass.java
new file mode 100644
index 0000000000..69dde53538
--- /dev/null
+++ b/test/java/clojure/test/ConcreteClass.java
@@ -0,0 +1,8 @@
+package clojure.test;
+
+public class ConcreteClass implements GenericInterface {
+ @Override
+ public Integer stampWidgets(Integer val) {
+ return 42;
+ }
+}
\ No newline at end of file
diff --git a/test/java/clojure/test/GenericInterface.java b/test/java/clojure/test/GenericInterface.java
new file mode 100644
index 0000000000..a20496ab1a
--- /dev/null
+++ b/test/java/clojure/test/GenericInterface.java
@@ -0,0 +1,5 @@
+package clojure.test;
+
+public interface GenericInterface {
+ T stampWidgets(T val);
+}
\ No newline at end of file
diff --git a/test/java/clojure/test/SwissArmy.java b/test/java/clojure/test/SwissArmy.java
new file mode 100644
index 0000000000..339c3723c4
--- /dev/null
+++ b/test/java/clojure/test/SwissArmy.java
@@ -0,0 +1,39 @@
+package clojure.test;
+
+public class SwissArmy {
+ public String ctorId;
+
+ public SwissArmy() {this.ctorId = "1";}
+ public SwissArmy(int a, long b) {this.ctorId = "2";}
+ public SwissArmy(long a, int b) {this.ctorId = "3";}
+ public SwissArmy(boolean a, boolean b) {this.ctorId = "4";}
+ public SwissArmy(long[] a, int b) {this.ctorId = "5";}
+ public SwissArmy(String[] a, long b) {this.ctorId = "6";}
+ public SwissArmy(int[][] a, long b) {this.ctorId = "7";}
+
+ public String noArgs() {return "";}
+ public String twoArgsIL(int a, long b) {return "int-long";}
+ public String twoArgsLI(long a, int b) {return "long-int";}
+ public String twoArgsBB(boolean a, boolean b) {return "boolean-boolean";}
+// public String twoArgsLAI(long[] a, int b) {return "long<>-int";}
+ public String twoArgsSAL(String[] a, long b) {return "java.lang.String<>-long";}
+// public String twoArgsMDIL(int[][] a, long b) {return "int<><>-long";}
+ public String arityOverloadMethod(int a) {return "int";}
+ public String arityOverloadMethod(int a, int b) {return "int-int";}
+ public String arityOverloadMethod(int a, int b, int c) {return "int-int-int";}
+ public String doppelganger(int a, int b) {return "int-int";}
+
+ public static String staticNoArgs() {return "";}
+ public static String staticOneArg(boolean a) {return "boolean";}
+ public static String staticTwoArgsIL(int a, long b) {return "int-long";}
+ public static String staticTwoArgsLI(long a, int b) {return "long-int";}
+ public static String staticTwoArgsBB(boolean a, boolean b) {return "boolean-boolean";}
+ public static String staticTwoArgsSAL(String[] a, long b) {return "java.lang.String<>-long";}
+// public static String staticTwoArgsMDIL(int[][] a, long b) {return "int<><>-long";}
+// public static String couldReflect(long[] a, int b) {return "long<>-int";}
+ public static String couldReflect(Object[] a, int b) {return "java.lang.Object<>-int";}
+ public static String staticArityOverloadMethod(int a) {return "int";}
+ public static String staticArityOverloadMethod(int a, int b) {return "int-int";}
+ public static String staticArityOverloadMethod(int a, int b, int c) {return "int-int-int";}
+ public static String doppelganger(int a, int b, long c) {return "int-int-long";}
+}
\ No newline at end of file
From b2a914355589acad3aed6d724fbd6a995fd2610b Mon Sep 17 00:00:00 2001
From: fogus
Date: Wed, 10 Jan 2024 11:01:32 -0600
Subject: [PATCH 171/285] CLJ-2807: Add support for a symbol comprising the
name of the array component type (primitive, full-qualified class, or import
aliases accepted) followed by an asterisk for each dimension of the array (1
or more).
---
src/jvm/clojure/lang/Compiler.java | 87 ++++++++++++++++++++--
test/clojure/test_clojure/java_interop.clj | 20 +++++
test/clojure/test_clojure/numbers.clj | 17 +++++
3 files changed, 119 insertions(+), 5 deletions(-)
diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java
index 4bb066c92d..4baf9cb72c 100644
--- a/src/jvm/clojure/lang/Compiler.java
+++ b/src/jvm/clojure/lang/Compiler.java
@@ -395,7 +395,12 @@ static Symbol resolveSymbol(Symbol sym){
}
Object o = currentNS().getMapping(sym);
if(o == null)
+ {
+ Class ac = HostExpr.maybeArrayClass(sym);
+ if(ac != null)
+ return HostExpr.arrayTypeToSymbol(ac);
return Symbol.intern(currentNS().name.name, sym.name);
+ }
else if(o instanceof Class)
return Symbol.intern(null, ((Class) o).getName());
else if(o instanceof Var)
@@ -1023,8 +1028,12 @@ public static Class maybeClass(Object form, boolean stringOk) {
{
if(Util.equals(sym,COMPILE_STUB_SYM.get()))
return (Class) COMPILE_STUB_CLASS.get();
- if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[')
- c = RT.classForNameNonLoading(sym.name);
+ else if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[')
+ {
+ c = maybeArrayClass(sym);
+ if (c == null)
+ c = RT.classForNameNonLoading(sym.name);
+ }
else
{
Object o = currentNS().getMapping(sym);
@@ -1035,7 +1044,12 @@ else if(LOCAL_ENV.deref() != null && ((java.util.Map)LOCAL_ENV.deref()).contains
else
{
try{
- c = RT.classForNameNonLoading(sym.name);
+ if(c == null)
+ {
+ c = maybeArrayClass(sym);
+ if(c == null)
+ c = RT.classForNameNonLoading(sym.name);
+ }
}
catch(Exception e){
// aargh
@@ -1097,6 +1111,60 @@ else if(sym.name.equals("booleans"))
return c;
}
+ static String getArrayComponentClassDescriptor(Class c) {
+ if (c.isPrimitive())
+ return Type.getType(c).getDescriptor();
+
+ return "L" + c.getName() + ";";
+ }
+
+ // componentArrayType (maybe qualified or prim) ending in 1+ *'s, e.g. java.lang.String**
+ // capture group 1 = componentArrayType, group 2 = dimension *'s
+ final static Pattern ARRAY_TYPE_PATTERN = Pattern.compile("([^*]+)(\\*+)");
+
+ public static Class maybeArrayClass(Symbol sym) {
+ if(!sym.name.endsWith("*")) return null;
+
+ Matcher matcher = ARRAY_TYPE_PATTERN.matcher(sym.name);
+
+ if(!matcher.matches()) return null;
+
+ Symbol rootSymbol = Symbol.intern(matcher.group(1));
+ String stars = matcher.group(2);
+ Class componentClass = maybeClass(rootSymbol, false);
+
+ if(componentClass == null)
+ componentClass = primClass(rootSymbol);
+
+ if(componentClass == null) return null;
+
+ String componentDescriptor = getArrayComponentClassDescriptor(componentClass);
+ StringBuilder arrayDescriptor = new StringBuilder();
+ arrayDescriptor.append(stars.replace('*', '['));
+ arrayDescriptor.append(componentDescriptor);
+ return maybeClass(arrayDescriptor.toString(), true);
+ }
+
+ public static Symbol arrayTypeToSymbol(Class c) {
+ if(!c.isArray()) return null;
+
+ int dim = 1;
+
+ Class componentClass = c.getComponentType();
+
+ while(componentClass.isArray()) {
+ dim++;
+ componentClass = componentClass.getComponentType();
+ }
+
+ String componentClassName = componentClass.getName();
+ StringBuilder repr = new StringBuilder(componentClassName.length() + dim);
+ repr.append(componentClassName);
+ for(int i=0; i 0 || sym.name.charAt(0) == '[')
{
- return RT.classForName(sym.name);
+ Class ac = HostExpr.maybeArrayClass(sym);
+ if(ac != null)
+ return ac;
+ else
+ return RT.classForName(sym.name);
}
else if(sym.equals(NS))
return RT.NS_VAR;
@@ -7732,7 +7804,12 @@ else if(sym.equals(IN_NS))
Object o = n.getMapping(sym);
if(o == null)
{
- if(RT.booleanCast(RT.ALLOW_UNRESOLVED_VARS.deref()))
+ o = HostExpr.maybeArrayClass(sym);
+ if(o != null)
+ {
+ return o;
+ }
+ else if(RT.booleanCast(RT.ALLOW_UNRESOLVED_VARS.deref()))
{
return sym;
}
diff --git a/test/clojure/test_clojure/java_interop.clj b/test/clojure/test_clojure/java_interop.clj
index da7075d138..dae092f641 100644
--- a/test/clojure/test_clojure/java_interop.clj
+++ b/test/clojure/test_clojure/java_interop.clj
@@ -639,3 +639,23 @@
(deftest test-boxing-prevention-when-compiling-statements
(is (= 1 (.get (doto (AtomicInteger. 0) inc-atomic-int))))
(is (= 1 (.get (doto (AtomicLong. 0) inc-atomic-long)))))
+
+(deftest array-type-symbols
+ (is (= long* (class (make-array Long/TYPE 0))))
+ (is (= int* (class (make-array Integer/TYPE 0))))
+ (is (= double* (class (make-array Double/TYPE 0))))
+ (is (= short* (class (make-array Short/TYPE 0))))
+ (is (= boolean* (class (make-array Boolean/TYPE 0))))
+ (is (= byte* (class (make-array Byte/TYPE 0))))
+ (is (= float* (class (make-array Float/TYPE 0))))
+ (is (= String* (class (make-array String 0))))
+ (is (= java.lang.String* (class (make-array String 0))))
+ (is (= java.util.UUID* (class (make-array java.util.UUID 0))))
+ (is (= `byte* 'byte*))
+ (is (= `byte*** 'byte***))
+ (is (= `java.util.UUID* 'java.util.UUID*))
+ (is (= `String* 'java.lang.String*))
+ (is (= `java.lang.String* 'java.lang.String*))
+ (is (= `[NotAClassThatWasImported*] '[clojure.test-clojure.java-interop/NotAClassThatWasImported*]))
+ (is (= [long**] `[~long**]))
+ (is (= [42] (let [long** 42] `[~long**]))))
diff --git a/test/clojure/test_clojure/numbers.clj b/test/clojure/test_clojure/numbers.clj
index be4f7a0a60..ec0fa04e34 100644
--- a/test/clojure/test_clojure/numbers.clj
+++ b/test/clojure/test_clojure/numbers.clj
@@ -593,6 +593,23 @@ Math/pow overflows to Infinity."
"[I" (int-array 1) (ints (int-array 1 1))
"[J" (long-array 1) (longs (long-array 1 1))))
+(deftest test-array-type-symbols
+ (are [str-repr klass] (= (Class/forName str-repr) klass)
+ "[Z" (clojure.lang.Compiler$HostExpr/maybeArrayClass 'boolean*)
+ "[B" (clojure.lang.Compiler$HostExpr/maybeArrayClass 'byte*)
+ "[C" (clojure.lang.Compiler$HostExpr/maybeArrayClass 'char*)
+ "[S" (clojure.lang.Compiler$HostExpr/maybeArrayClass 'short*)
+ "[F" (clojure.lang.Compiler$HostExpr/maybeArrayClass 'float*)
+ "[D" (clojure.lang.Compiler$HostExpr/maybeArrayClass 'double*)
+ "[I" (clojure.lang.Compiler$HostExpr/maybeArrayClass 'int*)
+ "[J" (clojure.lang.Compiler$HostExpr/maybeArrayClass 'long*)
+ "[[J" (clojure.lang.Compiler$HostExpr/maybeArrayClass 'long**)
+ "[Ljava.lang.Object;" (clojure.lang.Compiler$HostExpr/maybeArrayClass 'Object*)
+ "[Ljava.lang.String;" (clojure.lang.Compiler$HostExpr/maybeArrayClass 'String*)
+ "[[Ljava.lang.String;" (clojure.lang.Compiler$HostExpr/maybeArrayClass 'String**))
+ (is (nil? (clojure.lang.Compiler$HostExpr/maybeArrayClass 'Object)))
+ (is (nil? (clojure.lang.Compiler$HostExpr/maybeArrayClass 'ThisIsNotAClassThatCouldBeFound138)))
+ (is (nil? (clojure.lang.Compiler$HostExpr/maybeArrayClass 'String-*IJ*))))
(deftest test-ratios
(is (== (denominator 1/2) 2))
From 619b576022cdd5fae899a8418bc568ab1dac3472 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Wed, 7 Feb 2024 11:48:07 -0600
Subject: [PATCH 172/285] CLJ-2775: Added support for java.util.stream streams
including stream-reduce, stream-seq, stream-transduce, and stream-into.
---
src/clj/clojure/core.clj | 42 ++++++++++-
src/clj/clojure/core/protocols.clj | 39 +++++-----
test/clojure/test_clojure/streams.clj | 103 ++++++++++++++++++++++++++
3 files changed, 162 insertions(+), 22 deletions(-)
create mode 100644 test/clojure/test_clojure/streams.clj
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index b8d9ff9f54..ebca84d40c 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -6830,8 +6830,6 @@ fails, attempts to require sym's namespace and retries."
`(let [~ge ~e] (case* ~ge ~shift ~mask ~default ~imap ~switch-type :hash-identity ~skip-check))))))))
-;; redefine reduce with internal-reduce
-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; helper files ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(alter-meta! (find-ns 'clojure.core) assoc :doc "Fundamental library of the Clojure language")
(load "core_proxy")
@@ -6841,6 +6839,45 @@ fails, attempts to require sym's namespace and retries."
(load "core/protocols")
(load "gvec")
+(defn stream-reduce!
+ "Works like reduce but takes a java.util.stream.BaseStream as its source.
+ Honors 'reduced', is a terminal operation on the stream"
+ {:added "1.12"}
+ ([f ^java.util.stream.BaseStream s]
+ (clojure.core.protocols/iterator-reduce! (.iterator s) f))
+ ([f init ^java.util.stream.BaseStream s]
+ (clojure.core.protocols/iterator-reduce! (.iterator s) f init)))
+
+(defn stream-seq!
+ "Takes a java.util.stream.BaseStream instance s and returns a seq of its
+ contents. This is a terminal operation on the stream."
+ {:added "1.12"}
+ [^java.util.stream.BaseStream stream]
+ (iterator-seq (.iterator stream)))
+
+(defn stream-transduce!
+ "Works like transduce but takes a java.util.stream.BaseStream as its source.
+ This is a terminal operation on the stream."
+ {:added "1.12"}
+ ([xform f ^java.util.stream.BaseStream stream] (stream-transduce! xform f (f) stream))
+ ([xform f init ^java.util.stream.BaseStream stream]
+ (let [f (xform f)
+ ret (stream-reduce! f init stream)]
+ (f ret))))
+
+(defn stream-into!
+ "Returns a new coll consisting of coll with all of the items of the
+ stream conjoined. This is a terminal operation on the stream."
+ {:added "1.12"}
+ ([to ^java.util.stream.BaseStream stream]
+ (if (instance? clojure.lang.IEditableCollection to)
+ (with-meta (persistent! (stream-reduce! conj! (transient to) stream)) (meta to))
+ (stream-reduce! conj to stream)))
+ ([to xform ^java.util.stream.BaseStream stream]
+ (if (instance? clojure.lang.IEditableCollection to)
+ (with-meta (persistent! (stream-transduce! xform conj! (transient to) stream)) (meta to))
+ (stream-transduce! xform conj to stream))))
+
(defmacro ^:private when-class [class-name & body]
`(try
(Class/forName ^String ~class-name)
@@ -6887,6 +6924,7 @@ fails, attempts to require sym's namespace and retries."
:added "1.11"}
^java.util.UUID [] (java.util.UUID/randomUUID))
+;; redefine reduce with internal-reduce
(defn reduce
"f should be a function of 2 arguments. If val is not supplied,
returns the result of applying f to the first 2 items in coll, then
diff --git a/src/clj/clojure/core/protocols.clj b/src/clj/clojure/core/protocols.clj
index 10c022911d..2ed43152b6 100644
--- a/src/clj/clojure/core/protocols.clj
+++ b/src/clj/clojure/core/protocols.clj
@@ -30,27 +30,26 @@
(let [s (seq coll)]
(internal-reduce s f val))))
-(defn- iter-reduce
- ([^java.lang.Iterable coll f]
- (let [iter (.iterator coll)]
+;; mutates the iterator, respects reduced
+(defn iterator-reduce!
+ ([^java.util.Iterator iter f]
+ (if (.hasNext iter)
+ (iterator-reduce! iter f (.next iter))
+ (f)))
+ ([^java.util.Iterator iter f val]
+ (loop [ret val]
(if (.hasNext iter)
- (loop [ret (.next iter)]
- (if (.hasNext iter)
- (let [ret (f ret (.next iter))]
- (if (reduced? ret)
- @ret
- (recur ret)))
- ret))
- (f))))
- ([^java.lang.Iterable coll f val]
- (let [iter (.iterator coll)]
- (loop [ret val]
- (if (.hasNext iter)
- (let [ret (f ret (.next iter))]
- (if (reduced? ret)
- @ret
- (recur ret)))
- ret)))))
+ (let [ret (f ret (.next iter))]
+ (if (reduced? ret)
+ @ret
+ (recur ret)))
+ ret))))
+
+(defn- iter-reduce
+ ([^Iterable coll f]
+ (iterator-reduce! (.iterator coll) f))
+ ([^Iterable coll f val]
+ (iterator-reduce! (.iterator coll) f val)))
(defn- naive-seq-reduce
"Reduces a seq, ignoring any opportunities to switch to a more
diff --git a/test/clojure/test_clojure/streams.clj b/test/clojure/test_clojure/streams.clj
new file mode 100644
index 0000000000..c5462a1aa0
--- /dev/null
+++ b/test/clojure/test_clojure/streams.clj
@@ -0,0 +1,103 @@
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html at the root of this distribution.
+; By using this software in any fashion, you are agreeing to be bound by
+; the terms of this license.
+; You must not remove this notice, or any other, from this software.
+
+(ns clojure.test-clojure.streams
+ (:use clojure.test)
+ (:import [java.util.stream Stream LongStream]
+ [java.util.function Consumer Predicate Supplier]))
+
+(deftest test-stream-reduce!
+ (is (= :only-val (stream-reduce! + (.stream [:only-val]))))
+ (is (= 0 (stream-reduce! + (.stream []))))
+ (is (= 5 (stream-reduce! + (.stream [1 4]))))
+ (is (= 6 (stream-reduce! + (.stream [1 2 3]))))
+ (is (= [2 3 4]
+ (stream-reduce! (fn [v i] (conj v (inc i)))
+ []
+ (.stream [1 2 3]))))
+
+ (is (= 15 (stream-reduce! + (LongStream/rangeClosed 1 5))))
+
+ (is (= 9 (stream-reduce! + (-> (Stream/of (to-array [1 2 3 4 5]))
+ (.filter (reify Predicate (test [_ v] (odd? v))))))))
+ (is (= {:a 1}
+ (meta
+ (stream-reduce! (fn [v i] (conj v (inc i)))
+ (with-meta [] {:a 1})
+ (.stream [1 2 3]))))))
+
+(deftest test-stream-reduced!
+ (is (= 5 (stream-reduce!
+ (fn [x y] (reduced (+ x y)))
+ (.stream [1 4]))))
+
+ (is (= 45 (stream-reduce!
+ (fn [acc v]
+ (if (= v 10)
+ (reduced acc)
+ (+ acc v)))
+ (LongStream/rangeClosed 1 10)))))
+
+(deftest stream-seq!-test
+ (let [none (.stream [])
+ one (Stream/of "a")
+ n (.stream ["a" "b" "c"])
+ inf (Stream/generate (reify Supplier (get [_] 42)))
+ st (stream-seq! one)
+ l100 (LongStream/range 0 100)]
+ (is (empty? (map identity (stream-seq! none))))
+ (is (seq? st))
+ (is (= ["a"] (map identity st)))
+ (is (= ["a" "b" "c"] (map identity (stream-seq! n))))
+ (is (= [42 42 42 42 42] (take 5 (stream-seq! inf))))
+ (is (= 4950 (reduce + (stream-seq! l100)))) 4950))
+
+(deftest stream-transduce!-test
+ (let [xf (comp (filter odd?) (take 5))]
+ (let [st (Stream/of (to-array [1 2 3 4 5 6 7 8 9]))]
+ (is (= [1 3 5 7 9] (stream-transduce! xf conj st))))
+
+ (let [inf (Stream/generate (reify Supplier (get [_] 0)))]
+ (is (empty? (stream-transduce! xf conj (.limit inf 50)))))
+
+ (let [inf (Stream/generate (reify Supplier (get [_] 43)))]
+ (is (= [43 43 43 43 43] (stream-transduce! xf conj (.limit inf 50)))))
+
+ (let [inf (Stream/generate (reify Supplier (get [_] 43)))]
+ (is (= 215 (stream-transduce! xf + (.limit inf 50)))))
+
+ (let [inf (Stream/generate (reify Supplier (get [_] 43)))]
+ (is (= 315 (stream-transduce! xf + 100 (.limit inf 50)))))
+
+ (let [inf (Stream/generate (reify Supplier (get [_] 43)))]
+ (is (= "4343434343" (stream-transduce! xf str (.limit inf 50)))))))
+
+(deftest stream-into!-test
+ (let [none (.stream [])
+ one (Stream/of "a")
+ n (.stream ["a" "b" "c"])
+ inf (Stream/generate (reify Supplier (get [_] 42)))
+ par (-> (LongStream/rangeClosed 1 10) .boxed .parallel)
+ xf (comp (map #(+ 2 %)) (filter odd?))
+ par2 (-> (LongStream/rangeClosed 1 10) .boxed .parallel)]
+ (is (empty? (stream-into! [] none)))
+ (is (= ["a"] (stream-into! [] one)))
+ (is (= ["a" "b" "c"] (stream-into! [] n)))
+ (is (= [42 42 42 42 42]
+ (stream-into! [] (.limit inf 5))))
+ (is (= [1 2 3 4 5 6 7 8 9 10]
+ (stream-into! [] par)))
+ (is (= {:a 1}
+ (meta
+ (stream-into! (with-meta [] {:a 1})
+ (.stream [1 2 3])))))
+ (is (= {:a 1}
+ (meta
+ (stream-into! (with-meta clojure.lang.PersistentQueue/EMPTY {:a 1})
+ (.stream [1 2 3])))))
+ (is (= [-1 -2 3 5 7 9 11] (stream-into! [-1 -2] xf par2)))))
From 8895fef132bd064032e737e55d54d9ba2c07a0c6 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Mon, 17 Jul 2023 21:14:16 -0500
Subject: [PATCH 173/285] CLJ-2568 clojure.walk now retains metadata when
walking lists and seqs
---
src/clj/clojure/walk.clj | 4 ++--
test/clojure/test_clojure/clojure_walk.clj | 11 +++++++++++
test/clojure/test_clojure/reader.cljc | 5 +++--
3 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/src/clj/clojure/walk.clj b/src/clj/clojure/walk.clj
index fe8fb631bd..0f027e7ad0 100644
--- a/src/clj/clojure/walk.clj
+++ b/src/clj/clojure/walk.clj
@@ -41,10 +41,10 @@ the sorting function."}
{:added "1.1"}
[inner outer form]
(cond
- (list? form) (outer (apply list (map inner form)))
+ (list? form) (outer (with-meta (apply list (map inner form)) (meta form)))
(instance? clojure.lang.IMapEntry form)
(outer (clojure.lang.MapEntry/create (inner (key form)) (inner (val form))))
- (seq? form) (outer (doall (map inner form)))
+ (seq? form) (outer (with-meta (doall (map inner form)) (meta form)))
(instance? clojure.lang.IRecord form)
(outer (reduce (fn [r x] (conj r (inner x))) form form))
(coll? form) (outer (into (empty form) (map inner form)))
diff --git a/test/clojure/test_clojure/clojure_walk.clj b/test/clojure/test_clojure/clojure_walk.clj
index 6c6f866332..fa2f3e6492 100644
--- a/test/clojure/test_clojure/clojure_walk.clj
+++ b/test/clojure/test_clojure/clojure_walk.clj
@@ -63,3 +63,14 @@
(let [coll [:html {:a ["b" 1]} ""]
f (fn [e] (if (and (vector? e) (not (map-entry? e))) (apply list e) e))]
(is (= (list :html {:a (list "b" 1)} "") (w/postwalk f coll)))))
+
+(defrecord RM [a])
+(deftest retain-meta
+ (let [m {:foo true}]
+ (are [o] (= m (meta (w/postwalk identity (with-meta o m))))
+ '(1 2)
+ [1 2]
+ #{1 2}
+ {1 2}
+ (map inc (range 3))
+ (->RM 1))))
\ No newline at end of file
diff --git a/test/clojure/test_clojure/reader.cljc b/test/clojure/test_clojure/reader.cljc
index 522a27928a..513b8dfe90 100644
--- a/test/clojure/test_clojure/reader.cljc
+++ b/test/clojure/test_clojure/reader.cljc
@@ -428,8 +428,9 @@
(doseq [form top-levels]
(clojure.walk/postwalk
#(when (list? %)
- (is (= (expected-metadata (first %))
- (meta %)))
+ (when (contains? expected-metadata (first %))
+ (is (= (expected-metadata (first %))
+ (meta %))))
(is (->> (meta %)
vals
(filter number?)
From 213c50e7a34a27901b6db835b87f840a8b2a36ee Mon Sep 17 00:00:00 2001
From: JarrodCTaylor
Date: Fri, 26 Jan 2024 13:22:37 -0600
Subject: [PATCH 174/285] clj-2803 update #inst printer to use thread safe
java.time.format.DateTimeFormatter
---
src/clj/clojure/instant.clj | 37 +++++++++++++++----------------------
1 file changed, 15 insertions(+), 22 deletions(-)
diff --git a/src/clj/clojure/instant.clj b/src/clj/clojure/instant.clj
index 2c052c03fa..3efc8b6026 100644
--- a/src/clj/clojure/instant.clj
+++ b/src/clj/clojure/instant.clj
@@ -159,22 +159,18 @@ with invalid arguments."
;;; ------------------------------------------------------------------------
;;; print integration
-
-(def ^:private ^ThreadLocal thread-local-utc-date-format
- ;; SimpleDateFormat is not thread-safe, so we use a ThreadLocal proxy for access.
- ;; http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4228335
- (proxy [ThreadLocal] []
- (initialValue []
- (doto (java.text.SimpleDateFormat. "yyyy-MM-dd'T'HH:mm:ss.SSS-00:00")
- ;; RFC3339 says to use -00:00 when the timezone is unknown (+00:00 implies a known GMT)
- (.setTimeZone (java.util.TimeZone/getTimeZone "GMT"))))))
+(def ^:private ^java.time.format.DateTimeFormatter UTC_DATE_FORMATTER
+ ;; RFC3339 says to use -00:00 when the timezone is unknown (+00:00 implies a known GMT)
+ (.withZone (java.time.format.DateTimeFormatter/ofPattern "yyyy-MM-dd'T'HH:mm:ss.SSS-00:00")
+ (java.time.ZoneId/of "GMT")))
(defn- print-date
"Print a java.util.Date as RFC3339 timestamp, always in UTC."
[^java.util.Date d, ^java.io.Writer w]
- (let [^java.text.DateFormat utc-format (.get thread-local-utc-date-format)]
+ (let [instant (.toInstant d)
+ formatted-date (.format UTC_DATE_FORMATTER instant)]
(.write w "#inst \"")
- (.write w (.format utc-format d))
+ (.write w formatted-date)
(.write w "\"")))
(defmethod print-method java.util.Date
@@ -206,23 +202,19 @@ with invalid arguments."
(print-calendar c w))
-(def ^:private ^ThreadLocal thread-local-utc-timestamp-format
- ;; SimpleDateFormat is not thread-safe, so we use a ThreadLocal proxy for access.
- ;; http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4228335
- (proxy [ThreadLocal] []
- (initialValue []
- (doto (java.text.SimpleDateFormat. "yyyy-MM-dd'T'HH:mm:ss")
- (.setTimeZone (java.util.TimeZone/getTimeZone "GMT"))))))
+(def ^:private ^java.time.format.DateTimeFormatter UTC_TIMESTAMP_FORMATTER
+ (.withZone (java.time.format.DateTimeFormatter/ofPattern "yyyy-MM-dd'T'HH:mm:ss.nnnnnnnnn")
+ (java.time.ZoneId/of "UTC")))
(defn- print-timestamp
"Print a java.sql.Timestamp as RFC3339 timestamp, always in UTC."
[^java.sql.Timestamp ts, ^java.io.Writer w]
- (let [^java.text.DateFormat utc-format (.get thread-local-utc-timestamp-format)]
+ (let [instant (.toInstant ts)
+ formatted-date (.format UTC_TIMESTAMP_FORMATTER instant)]
(.write w "#inst \"")
- (.write w (.format utc-format ts))
- ;; add on nanos and offset
+ (.write w formatted-date)
;; RFC3339 says to use -00:00 when the timezone is unknown (+00:00 implies a known GMT)
- (.write w (format ".%09d-00:00" (.getNanos ts)))
+ (.write w "-00:00")
(.write w "\"")))
(defmethod print-method java.sql.Timestamp
@@ -292,3 +284,4 @@ fractional seconds with nanosecond precision. The timezone offset will
be used to convert into UTC."
[^CharSequence cs]
(parse-timestamp (validated construct-timestamp) cs))
+
From 92b8fc7769bdb3be1b572b4c0c879d5a968a2557 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Tue, 6 Jul 2021 13:44:15 -0500
Subject: [PATCH 175/285] CLJ-2640 ex-info does not fail on nil data
---
src/jvm/clojure/lang/ExceptionInfo.java | 6 +-----
test/clojure/test_clojure/errors.clj | 6 +++---
2 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/src/jvm/clojure/lang/ExceptionInfo.java b/src/jvm/clojure/lang/ExceptionInfo.java
index f7b704deab..28f845b7c3 100644
--- a/src/jvm/clojure/lang/ExceptionInfo.java
+++ b/src/jvm/clojure/lang/ExceptionInfo.java
@@ -28,11 +28,7 @@ public ExceptionInfo(String s, IPersistentMap data) {
public ExceptionInfo(String s, IPersistentMap data, Throwable throwable) {
// null cause is equivalent to not passing a cause
super(s, throwable);
- if (data != null) {
- this.data = data;
- } else {
- throw new IllegalArgumentException("Additional data must be non-nil.");
- }
+ this.data = (data == null) ? PersistentArrayMap.EMPTY: data;
}
public IPersistentMap getData() {
diff --git a/test/clojure/test_clojure/errors.clj b/test/clojure/test_clojure/errors.clj
index cebe0392e3..0ee958786d 100644
--- a/test/clojure/test_clojure/errors.clj
+++ b/test/clojure/test_clojure/errors.clj
@@ -107,9 +107,9 @@
(with-out-str (pr t))
(catch Throwable t (is nil)))))))
-(deftest ex-info-disallows-nil-data
- (is (thrown? IllegalArgumentException (ex-info "message" nil)))
- (is (thrown? IllegalArgumentException (ex-info "message" nil (Throwable. "cause")))))
+(deftest ex-info-allows-nil-data
+ (is (= {} (ex-data (ex-info "message" nil))))
+ (is (= {} (ex-data (ex-info "message" nil (Throwable. "cause"))))))
(deftest ex-info-arities-construct-equivalent-exceptions
(let [ex1 (ex-info "message" {:foo "bar"})
From 39ac70c82ea0c2e5c15d992d319d65523bc60b59 Mon Sep 17 00:00:00 2001
From: JarrodCTaylor
Date: Thu, 26 Oct 2023 16:06:50 -0500
Subject: [PATCH 176/285] CLJ-2777 Add :clear-env option to c.j.process/start
---
src/clj/clojure/java/process.clj | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/src/clj/clojure/java/process.clj b/src/clj/clojure/java/process.clj
index 74f87af7b6..5974f6325d 100644
--- a/src/clj/clojure/java/process.clj
+++ b/src/clj/clojure/java/process.clj
@@ -56,13 +56,17 @@
(ProcessBuilder$Redirect/from (jio/file f)))
(defn start
- "Starts an external command as args and optional leading opts map:
+ "Start an external command, defined in args.
+ The process environment vars are inherited from the parent by
+ default (use :clear-env to clear them).
+ If needed, provide options in map as first arg:
:in - a ProcessBuilder.Redirect (default = :pipe) or :inherit
:out - a ProcessBuilder.Redirect (default = :pipe) or :inherit :discard
:err - a ProcessBuilder.Redirect (default = :pipe) or :inherit :discard :stdout
- :dir - directory to run the command from, default=\".\"
- :env - {env-var value} of environment variables (all strings)
+ :dir - current directory when the process runs (default=\".\")
+ :clear-env - if true, remove all inherited parent env vars
+ :env - {env-var value} of environment variables to set (all strings)
Returns an ILookup containing the java.lang.Process in :process and the
streams :in :out :err. The map is also an IDeref that waits for process exit
@@ -72,7 +76,7 @@
(let [[opts command] (if (map? (first opts+args))
[(first opts+args) (rest opts+args)]
[{} opts+args])
- {:keys [in out err dir env]
+ {:keys [in out err dir env clear-env]
:or {in :pipe, out :pipe, err :pipe, dir "."}} opts
pb (ProcessBuilder. ^List command)
to-redirect (fn to-redirect
@@ -89,6 +93,8 @@
(if
(= err :stdout) (.redirectErrorStream pb true)
(.redirectError pb ^ProcessBuilder$Redirect (to-redirect err)))
+ (when clear-env
+ (.clear (.environment pb)))
(when env
(let [pb-env (.environment pb)]
(run! (fn [[k v]] (.put pb-env k v)) env)))
From 882d660114c95b9959ee6124a0f1a420d19d8d4f Mon Sep 17 00:00:00 2001
From: JarrodCTaylor
Date: Tue, 24 Oct 2023 17:05:45 -0500
Subject: [PATCH 177/285] CLJ-2783 Update deprecated process of creating URLs
in Java 20+
---
src/clj/clojure/java/io.clj | 16 +++++++++-------
src/clj/clojure/repl/deps.clj | 6 +++---
src/jvm/clojure/lang/RT.java | 18 ++++++++++++------
test/clojure/test_clojure/java/io.clj | 3 ++-
4 files changed, 26 insertions(+), 17 deletions(-)
diff --git a/src/clj/clojure/java/io.clj b/src/clj/clojure/java/io.clj
index f0715abfd4..1f6d121bc1 100644
--- a/src/clj/clojure/java/io.clj
+++ b/src/clj/clojure/java/io.clj
@@ -12,6 +12,7 @@
clojure.java.io
(:require clojure.string)
(:import
+ (clojure.lang RT)
(java.io Reader InputStream InputStreamReader PushbackReader
BufferedReader File OutputStream
OutputStreamWriter BufferedWriter Writer
@@ -19,7 +20,8 @@
StringReader ByteArrayInputStream
BufferedInputStream BufferedOutputStream
CharArrayReader Closeable)
- (java.net URI URL MalformedURLException Socket URLDecoder URLEncoder)))
+ (java.lang IllegalArgumentException)
+ (java.net URI URL Socket URLDecoder URLEncoder)))
(set! *warn-on-reflection* true)
@@ -50,11 +52,11 @@
String
(as-file [s] (File. s))
- (as-url [s] (URL. s))
+ (as-url [s] (RT/toUrl s))
File
(as-file [f] f)
- (as-url [f] (.toURL (.toURI f)))
+ (as-url [f] (RT/toUrl f))
URL
(as-url [u] u)
@@ -255,13 +257,13 @@
(assoc default-streams-impl
:make-input-stream (fn [^String x opts]
(try
- (make-input-stream (URL. x) opts)
- (catch MalformedURLException e
+ (make-input-stream (RT/toUrl x) opts)
+ (catch IllegalArgumentException e
(make-input-stream (File. x) opts))))
:make-output-stream (fn [^String x opts]
(try
- (make-output-stream (URL. x) opts)
- (catch MalformedURLException err
+ (make-output-stream (RT/toUrl x) opts)
+ (catch IllegalArgumentException err
(make-output-stream (File. x) opts))))))
(extend Socket
diff --git a/src/clj/clojure/repl/deps.clj b/src/clj/clojure/repl/deps.clj
index 1ed8365a1c..dfd3137fcd 100644
--- a/src/clj/clojure/repl/deps.clj
+++ b/src/clj/clojure/repl/deps.clj
@@ -14,7 +14,7 @@
[clojure.java.basis.impl :as basis-impl]
[clojure.tools.deps.interop :as tool])
(:import
- [clojure.lang DynamicClassLoader]
+ [clojure.lang DynamicClassLoader RT]
[java.io File]))
(set! *warn-on-reflection* true)
@@ -22,7 +22,7 @@
(defn- add-loader-url
"Add url string or URL to the highest level DynamicClassLoader url set."
[url]
- (let [u (if (string? url) (java.net.URL. url) url)
+ (let [u (if (string? url) (RT/toUrl ^String url) url)
loader (loop [loader (.getContextClassLoader (Thread/currentThread))]
(let [parent (.getParent loader)]
(if (instance? DynamicClassLoader parent)
@@ -48,7 +48,7 @@
{:keys [added] :as _res} (tool/invoke-tool {:tool-alias :deps, :fn 'clojure.tools.deps/resolve-added-libs, :args tool-args})
;_ (clojure.pprint/pprint _res)
paths (mapcat :paths (vals added))
- urls (->> paths (map jio/file) (map #(.toURL ^File %)))]
+ urls (->> paths (map jio/file) (map #(RT/toUrl ^File %)))]
(run! add-loader-url urls)
(basis-impl/update-basis! update :libs merge added)
(let [ret (-> added keys sort vec)]
diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java
index 86c89d69fb..0ff3e50a2b 100644
--- a/src/jvm/clojure/lang/RT.java
+++ b/src/jvm/clojure/lang/RT.java
@@ -12,7 +12,7 @@
package clojure.lang;
-import java.net.MalformedURLException;
+import java.net.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.Callable;
import java.util.*;
@@ -20,14 +20,12 @@
import java.util.regex.Pattern;
import java.io.*;
import java.lang.reflect.Array;
+import java.lang.IllegalArgumentException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.AccessController;
import java.security.PrivilegedAction;
-import java.net.URL;
-import java.net.JarURLConnection;
import java.nio.charset.Charset;
-import java.net.URLConnection;
public class RT{
@@ -293,8 +291,16 @@ private Object readResolve() throws ObjectStreamException {
static AtomicInteger id = new AtomicInteger(1);
-static public void addURL(Object url) throws MalformedURLException{
- URL u = (url instanceof String) ? (new URL((String) url)) : (URL) url;
+static public URL toUrl(String url) throws URISyntaxException, MalformedURLException {
+ return new URI(url).toURL();
+}
+
+static public URL toUrl(File file) throws MalformedURLException {
+ return file.toURI().toURL();
+}
+
+static public void addURL(Object url) throws MalformedURLException, URISyntaxException, IllegalArgumentException {
+ URL u = (url instanceof String) ? toUrl((String) url) : (URL) url;
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if(ccl instanceof DynamicClassLoader)
((DynamicClassLoader)ccl).addURL(u);
diff --git a/test/clojure/test_clojure/java/io.clj b/test/clojure/test_clojure/java/io.clj
index b83c63d311..fb179e96da 100644
--- a/test/clojure/test_clojure/java/io.clj
+++ b/test/clojure/test_clojure/java/io.clj
@@ -13,6 +13,7 @@
FileInputStream InputStreamReader InputStream
FileOutputStream OutputStreamWriter OutputStream
ByteArrayInputStream ByteArrayOutputStream)
+ (clojure.lang RT)
(java.net URL URI Socket ServerSocket)))
(defn temp-file
@@ -48,7 +49,7 @@
(slurp read-from :encoding "UTF-8")))
f f
(.getAbsolutePath f) (.getAbsolutePath f)
- (.toURL f) (.toURL f)
+ (RT/toUrl f) (RT/toUrl f)
(.toURI f) (.toURI f)
(FileOutputStream. f) (FileInputStream. f)
(OutputStreamWriter. (FileOutputStream. f) "UTF-8") (reader f :encoding "UTF-8")
From aa2c637f9512d14bd78ee30d6d030bba6d095e7d Mon Sep 17 00:00:00 2001
From: JarrodCTaylor
Date: Thu, 25 Jan 2024 12:57:48 -0600
Subject: [PATCH 178/285] CLJ-2828 Remove capture function
---
src/clj/clojure/java/process.clj | 22 +++++-----------------
src/clj/clojure/tools/deps/interop.clj | 2 +-
test/clojure/test_clojure/java/process.clj | 2 +-
3 files changed, 7 insertions(+), 19 deletions(-)
diff --git a/src/clj/clojure/java/process.clj b/src/clj/clojure/java/process.clj
index 5974f6325d..4355e5ec65 100644
--- a/src/clj/clojure/java/process.clj
+++ b/src/clj/clojure/java/process.clj
@@ -14,10 +14,9 @@
(if available) and the Java Process object. It is also deref-able to wait for
process exit.
- Helper functions are available to 'capture' the output of the process stdout
- and to wait for an 'ok?' non-error exit. The 'exec' function handles the common
- case of `start'ing a process, waiting for process exit, capture and return
- stdout."
+ Use ‘slurp' to capture the output of a process stream, and 'ok?’ to wait for a
+ non-error exit. The 'exec' function handles the common case of `start'ing a
+ process, waiting for process exit, slurp, and return stdout."
(:require
[clojure.java.io :as jio]
[clojure.string :as str])
@@ -121,17 +120,6 @@
[process-map]
(zero? (.waitFor ^Process (:process process-map))))
-(defn capture
- "Read from input-stream until EOF and return a String (or nil if 0 length).
- Takes same opts as clojure.java.io/copy - :buffer-size and :encoding"
- {:added "1.12"}
- [input-stream & opts]
- (let [writer (StringWriter.)]
- (apply jio/copy input-stream writer opts)
- (let [s (str/trim (.toString writer))]
- (when-not (zero? (.length s))
- s))))
-
;; A thread factory for daemon threads
(defonce ^:private io-thread-factory
(let [counter (atom 0)]
@@ -177,7 +165,7 @@
[{} opts+args])
opts (merge {:err :inherit} opts)]
(let [state (apply start opts command)
- captured (io-task #(capture (:out state)))]
+ captured (io-task #(slurp (:out state)))]
(if (ok? state)
@captured
(throw (RuntimeException. (str "Process failed with exit=" (.exitValue ^Process (:process state)))))))))
@@ -190,7 +178,7 @@
@(start {:out (to-file "out") :err (to-file "err")} "ls" "-l")
;; capture output to string
- (-> (start "ls" "-l") :out capture)
+ (-> (start "ls" "-l") :out slurp)
;; with exec
(exec "ls" "-l")
diff --git a/src/clj/clojure/tools/deps/interop.clj b/src/clj/clojure/tools/deps/interop.clj
index 3d0bdf1d7a..25d0f2548d 100644
--- a/src/clj/clojure/tools/deps/interop.clj
+++ b/src/clj/clojure/tools/deps/interop.clj
@@ -39,7 +39,7 @@
(doseq [a args]
(.write w (pr-str a))
(.write w " "))))
- (let [envelope (edn/read-string (proc/capture out))]
+ (let [envelope (edn/read-string (slurp out))]
(if preserve-envelope
envelope
(let [{:keys [tag val]} envelope
diff --git a/test/clojure/test_clojure/java/process.clj b/test/clojure/test_clojure/java/process.clj
index 170e801cd1..a36f211681 100644
--- a/test/clojure/test_clojure/java/process.clj
+++ b/test/clojure/test_clojure/java/process.clj
@@ -17,7 +17,7 @@
(is (not (str/blank? (p/exec "bash" "-c" "ls"))))
;; print to stderr, capture nil
- (is (nil? (p/exec "bash" "-c" "ls >&2")))
+ (is (str/blank? (p/exec "bash" "-c" "ls >&2")))
;; redirect, then capture to string
(is (not (str/blank? (p/exec {:err :stdout} "bash" "-c" "ls >&2")))))
From 5c6c21688154bd8985610712c958359d774c75d0 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 24 Aug 2023 10:13:14 -0500
Subject: [PATCH 179/285] CLJ-2791 Implement spliterator on IPersistentVector
---
src/jvm/clojure/lang/APersistentVector.java | 62 +++++++++++++++++
src/jvm/clojure/lang/PersistentVector.java | 73 +++++++++++++++++++++
test/clojure/test_clojure/vectors.clj | 64 +++++++++++++++++-
3 files changed, 198 insertions(+), 1 deletion(-)
diff --git a/src/jvm/clojure/lang/APersistentVector.java b/src/jvm/clojure/lang/APersistentVector.java
index 07115684aa..20c40918eb 100644
--- a/src/jvm/clojure/lang/APersistentVector.java
+++ b/src/jvm/clojure/lang/APersistentVector.java
@@ -14,6 +14,7 @@
import java.io.Serializable;
import java.util.*;
+import java.util.function.Consumer;
public abstract class APersistentVector extends AFn implements IPersistentVector, Iterable,
List,
@@ -276,6 +277,62 @@ public void remove(){
};
}
+Spliterator rangedSpliterator(final int start, final int end) {
+ return new Spliterator() {
+ int i = start;
+
+ @Override
+ public int characteristics() {
+ return Spliterator.IMMUTABLE | // persistent
+ Spliterator.ORDERED | // know order
+ Spliterator.SIZED | // know size
+ Spliterator.SUBSIZED; // know size after split
+ }
+
+ @Override
+ public long estimateSize() {
+ return end-i;
+ }
+
+ @Override
+ public long getExactSizeIfKnown() {
+ return end-i;
+ }
+
+ @Override
+ public boolean tryAdvance(Consumer action) {
+ if(i < end) {
+ action.accept(nth(i++));
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Spliterator trySplit() {
+ int lo = i;
+ int mid = (lo + end) >>> 1; // avoid overflow
+ if(lo >= mid) {
+ return null;
+ } else {
+ i = mid;
+ return rangedSpliterator(lo, mid);
+ }
+ }
+
+ @Override
+ public void forEachRemaining(Consumer action) {
+ for(int x=i; x>> 1; // avoid overflow
+ if(lo >= mid) {
+ return null;
+ } else {
+ i = mid;
+ return rangedSpliterator(lo, mid);
+ }
+ }
+
+ @Override
+ public void forEachRemaining(Consumer action) {
+ int x=i;
+ while(x 0)
diff --git a/test/clojure/test_clojure/vectors.clj b/test/clojure/test_clojure/vectors.clj
index ea2b8e77d5..97cc0c5add 100644
--- a/test/clojure/test_clojure/vectors.clj
+++ b/test/clojure/test_clojure/vectors.clj
@@ -9,7 +9,11 @@
; Author: Stuart Halloway, Daniel Solano Gómez
(ns clojure.test-clojure.vectors
- (:use clojure.test))
+ (:use clojure.test)
+ (:import
+ [java.util Collection Spliterator]
+ [java.util.function Consumer]
+ [java.util.stream Collectors]))
(deftest test-reversed-vec
(let [r (range 6)
@@ -428,3 +432,61 @@
(is (= [0 1 2] (new java.util.ArrayList [0 1 2])))
(is (not= [1 2] (take 1 (cycle [1 2]))))
(is (= [1 2 3 nil 4 5 6 nil] (eduction cat [[1 2 3 nil] [4 5 6 nil]]))))
+
+(set! *warn-on-reflection* true)
+
+;; remember returns a consumer that adds to an-atom of coll
+(defn remember
+ ^Consumer [an-atom]
+ (reify Consumer (accept [_ v] (swap! an-atom conj v))))
+
+(deftest test-empty-vector-spliterator
+ (let [v []
+ s (.spliterator ^Collection v)
+ seen (atom [])]
+ (is (= 0 (.estimateSize s) (.getExactSizeIfKnown s)))
+ (is (nil? (.trySplit s)))
+ (is (false? (.tryAdvance s (remember seen))))
+ (is (= @seen []))))
+
+;; tryAdvance then forEachRemaining walks vector spliterator
+(deftest test-spliterator-tryadvance-then-forEach
+ (let [n 66
+ source-vec (vec (range n))]
+ (for [v (into [(vec (range n))] (map #(subvec source-vec % (+ % 33)) (range 33)))]
+ (dotimes [up-to n]
+ (let [s (.spliterator ^Collection v)
+ seen (atom [])
+ consumer (remember seen)]
+ (loop [i 0]
+ (if (< i up-to)
+ (do (is (true? (.tryAdvance s consumer))) (recur (inc i)))
+ (.forEachRemaining s consumer)))
+ (is (= v @seen))
+ (is (false? (.tryAdvance s consumer))))))))
+
+;; recursively split vector spliterators, walk all of the splits
+(deftest test-spliterator-trySplit
+ (dotimes [n 257]
+ (let [v (vec (range n))
+ seen (atom #{})
+ consumer (remember seen)
+ splits (loop [ss [(.spliterator ^Collection v)]]
+ (let [ss' (map #(.trySplit ^Spliterator %) ss)]
+ (if (every? nil? ss')
+ ss
+ (recur (into ss (remove nil? ss'))))))]
+ (loop [[spl & sr] splits]
+ (when spl
+ (.forEachRemaining ^Spliterator spl consumer)
+ (recur sr)))
+ (is (= v (sort @seen))))))
+
+(deftest test-vector-parallel-stream
+ (dotimes [n 1024]
+ (let [v (vec (range n))]
+ (is (= n
+ (-> ^Collection v .stream (.collect (Collectors/counting)))
+ (-> ^Collection v .parallelStream (.collect (Collectors/counting)))
+ (-> v ^Collection (subvec 0 n) .stream (.collect (Collectors/counting)))
+ (-> v ^Collection (subvec 0 n) .parallelStream (.collect (Collectors/counting))))))))
\ No newline at end of file
From a0573522e7c5e5d0fd281eefd3d7d6686935b5cb Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Wed, 6 Sep 2023 18:50:03 -0500
Subject: [PATCH 180/285] CLJ-2792 Make IDeref extend Supplier interfaces and
provide default methods
---
src/jvm/clojure/lang/IDeref.java | 28 +++++++++++++++++++++++++++-
test/clojure/test_clojure/atoms.clj | 16 ++++++++++++++++
test/clojure/test_clojure/delays.clj | 20 ++++++++++++++++++++
3 files changed, 63 insertions(+), 1 deletion(-)
diff --git a/src/jvm/clojure/lang/IDeref.java b/src/jvm/clojure/lang/IDeref.java
index 3a4746157b..41002f8ae1 100644
--- a/src/jvm/clojure/lang/IDeref.java
+++ b/src/jvm/clojure/lang/IDeref.java
@@ -12,6 +12,32 @@
package clojure.lang;
-public interface IDeref{
+import java.util.function.BooleanSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+
+public interface IDeref extends Supplier, BooleanSupplier, IntSupplier, LongSupplier {
Object deref() ;
+
+@Override
+default Object get() {
+ return deref();
+}
+
+@Override
+default boolean getAsBoolean() {
+ return RT.booleanCast(deref());
+}
+
+@Override
+default int getAsInt() {
+ return RT.intCast(deref());
+}
+
+@Override
+default long getAsLong() {
+ return RT.longCast(deref());
+}
+
}
diff --git a/test/clojure/test_clojure/atoms.clj b/test/clojure/test_clojure/atoms.clj
index f9ecadcc56..2e6974ef03 100644
--- a/test/clojure/test_clojure/atoms.clj
+++ b/test/clojure/test_clojure/atoms.clj
@@ -42,3 +42,19 @@
(deftest reset-on-deref-reset-equality
(let [a (atom :usual-value)]
(is (= :usual-value (reset! a (first (reset-vals! a :almost-never-seen-value)))))))
+
+(deftest atoms-are-suppliers
+ (let [a (atom 10)]
+ (is (instance? java.util.function.Supplier a))
+ (is (= 10 (.get ^java.util.function.Supplier a)))
+ (swap! a inc)
+ (is (= 11 (.get ^java.util.function.Supplier a)))
+
+ (is (instance? java.util.function.IntSupplier a))
+ (is (= 11 (.getAsInt a)))
+
+ (is (instance? java.util.function.LongSupplier a))
+ (is (= 11 (.getAsLong a)))
+
+ (is (instance? java.util.function.BooleanSupplier a))
+ (is (true? (.getAsBoolean a)))))
\ No newline at end of file
diff --git a/test/clojure/test_clojure/delays.clj b/test/clojure/test_clojure/delays.clj
index 0a2a1c99ff..477ab99af6 100644
--- a/test/clojure/test_clojure/delays.clj
+++ b/test/clojure/test_clojure/delays.clj
@@ -66,3 +66,23 @@
(.await barrier)
(is (instance? Exception (try-call)))
(is (identical? (try-call) (try-call)))))
+
+(deftest delays-are-suppliers
+ (let [dt (delay true)
+ df (delay false)
+ dn (delay nil)
+ di (delay 100)]
+
+ (is (instance? java.util.function.Supplier dt))
+ (is (instance? java.util.function.BooleanSupplier dt))
+ (is (true? (.get dt)))
+ (is (true? (.getAsBoolean dt)))
+ (is (false? (.getAsBoolean df)))
+ (is (false? (.getAsBoolean dn)))
+
+ (is (instance? java.util.function.Supplier di))
+ (is (instance? java.util.function.IntSupplier di))
+ (is (instance? java.util.function.LongSupplier di))
+ (is (= 100 (.get ^java.util.function.Supplier di)))
+ (is (= 100 (.getAsInt ^java.util.function.IntSupplier di)))
+ (is (= 100 (.getAsLong ^java.util.function.LongSupplier di)))))
\ No newline at end of file
From 96c6f816ade9da63243ef6f55ace96bf15c275a8 Mon Sep 17 00:00:00 2001
From: Fogus
Date: Thu, 1 Feb 2024 06:15:55 -0600
Subject: [PATCH 181/285] CLJ-2290: Modified into docstring to mention 0 and
1-arities.
---
src/clj/clojure/core.clj | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index ebca84d40c..21e5c2c0a8 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -7008,8 +7008,9 @@ fails, attempts to require sym's namespace and retries."
(f ret))))
(defn into
- "Returns a new coll consisting of to-coll with all of the items of
- from-coll conjoined. A transducer may be supplied."
+ "Returns a new coll consisting of to with all of the items of
+ from conjoined. A transducer may be supplied.
+ (into x) returns x. (into) returns []."
{:added "1.0"
:static true}
([] [])
From dbaa1898adc82abaefb914277c791a2f8f97ef53 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Mon, 23 Oct 2023 14:01:56 -0500
Subject: [PATCH 182/285] CLJ-2225 Improve docstrings of assert and *assert*
---
src/clj/clojure/core.clj | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 21e5c2c0a8..d560f67d57 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -4853,8 +4853,11 @@
(.getCause ^Throwable ex)))
(defmacro assert
- "Evaluates expr and throws an exception if it does not evaluate to
- logical true."
+ "Evaluates expression x and throws an AssertionError with optional
+ message if x does not evaluate to logical true.
+
+ Assertion checks are omitted from compiled code if '*assert*' is
+ false."
{:added "1.0"}
([x]
(when *assert*
@@ -6587,6 +6590,11 @@ fails, attempts to require sym's namespace and retries."
"
{:added "1.0"})
+(add-doc-and-meta *assert*
+ "When set to logical false, 'assert' will omit assertion checks in
+ compiled code. Defaults to true."
+ {:added "1.0"})
+
(defn future?
"Returns true if x is a future"
{:added "1.1"
From 51d0c55ecc87bb79e7110b5f9162ef9d4adb734d Mon Sep 17 00:00:00 2001
From: Ambrose Bonnaire-Sergeant
Date: Fri, 26 Jan 2024 13:10:24 -0600
Subject: [PATCH 183/285] CLJ-2552: Fix example code and improve recur
description in reify docstring
---
src/clj/clojure/core_deftype.clj | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/src/clj/clojure/core_deftype.clj b/src/clj/clojure/core_deftype.clj
index c2babab291..17fce7012d 100644
--- a/src/clj/clojure/core_deftype.clj
+++ b/src/clj/clojure/core_deftype.clj
@@ -101,18 +101,19 @@
same arity in an interface you must specify complete hints to
disambiguate - a missing hint implies Object.
- recur works to method heads The method bodies of reify are lexical
- closures, and can refer to the surrounding local scope:
+ Method heads are recursion points for recur, as in a fn. The method
+ bodies of reify are lexical closures, and can refer to the surrounding
+ local scope:
(str (let [f \"foo\"]
- (reify Object
- (toString [this] f))))
+ (reify Object
+ (toString [this] f))))
== \"foo\"
(seq (let [f \"foo\"]
- (reify clojure.lang.Seqable
- (seq [this] (seq f)))))
- == (\\f \\o \\o))
+ (reify clojure.lang.Seqable
+ (seq [this] (seq f)))))
+ == (\\f \\o \\o)
reify always implements clojure.lang.IObj and transfers meta
data of the form to the created object.
From 68a53fad91071f551065d5387c21b5997ebc8c06 Mon Sep 17 00:00:00 2001
From: Christophe Grand
Date: Wed, 15 Nov 2023 12:49:10 -0600
Subject: [PATCH 184/285] CLJ-2813 Anonymous args accept non-integers, also
allows discarded content
---
src/jvm/clojure/lang/LispReader.java | 24 +++++++++---------------
test/clojure/test_clojure/reader.cljc | 21 ++++++++++++++++++++-
2 files changed, 29 insertions(+), 16 deletions(-)
diff --git a/src/jvm/clojure/lang/LispReader.java b/src/jvm/clojure/lang/LispReader.java
index 104b05d1a2..995e21be15 100644
--- a/src/jvm/clojure/lang/LispReader.java
+++ b/src/jvm/clojure/lang/LispReader.java
@@ -71,6 +71,7 @@ public class LispReader{
"([-+]?)(?:(0)|([1-9][0-9]*)|0[xX]([0-9A-Fa-f]+)|0([0-7]+)|([1-9][0-9]?)[rR]([0-9A-Za-z]+)|0[0-9]+)(N)?");
static Pattern ratioPat = Pattern.compile("([-+]?[0-9]+)/([0-9]+)");
static Pattern floatPat = Pattern.compile("([-+]?[0-9]+(\\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?");
+static Pattern argPat = Pattern.compile("%(?:(&)|([1-9][0-9]*))?");
//static Pattern accessorPat = Pattern.compile("\\.[a-zA-Z_]\\w*");
//static Pattern instanceMemberPat = Pattern.compile("\\.([a-zA-Z_][\\w\\.]*)\\.([a-zA-Z_]\\w*)");
//static Pattern staticMemberPat = Pattern.compile("([a-zA-Z_][\\w\\.]*)\\.([a-zA-Z_]\\w*)");
@@ -920,23 +921,16 @@ static Symbol registerArg(int n){
static class ArgReader extends AFn{
public Object invoke(Object reader, Object pct, Object opts, Object pendingForms) {
PushbackReader r = (PushbackReader) reader;
+ String token = readToken(r, '%');
if(ARG_ENV.deref() == null)
- {
- return interpretToken(readToken(r, '%'), null);
- }
- int ch = read1(r);
- unread(r, ch);
- //% alone is first arg
- if(ch == -1 || isWhitespace(ch) || isTerminatingMacro(ch))
- {
- return registerArg(1);
- }
- Object n = read(r, true, null, true, opts, ensurePending(pendingForms));
- if(n.equals(Compiler._AMP_))
- return registerArg(-1);
- if(!(n instanceof Number))
+ return interpretToken(token, null);
+
+ Matcher m = argPat.matcher(token);
+ if (!m.matches())
throw new IllegalStateException("arg literal must be %, %& or %integer");
- return registerArg(((Number) n).intValue());
+ if (m.group(1) != null) // %&
+ return registerArg(-1);
+ return registerArg(m.group(2) == null ? 1 : Integer.parseInt(m.group(2)));
}
}
diff --git a/test/clojure/test_clojure/reader.cljc b/test/clojure/test_clojure/reader.cljc
index 513b8dfe90..a0e1f1f13e 100644
--- a/test/clojure/test_clojure/reader.cljc
+++ b/test/clojure/test_clojure/reader.cljc
@@ -454,7 +454,26 @@
;; Anonymous function literal (#())
-(deftest t-Anonymouns-function-literal)
+(deftest t-Anonymous-function-literal
+ ;; #(vector %) => #(fn* [gen__#] (vector gen__#))
+ ;; [\S]+ matches the anon arg, then \1 is backref matching first group
+ (is (= "(fn* [] (vector))" (pr-str (read-string "#(vector)"))))
+ (is (not (nil? (re-matches #"\(fn\* \[([\S]+)] \(vector \1\)\)" (pr-str (read-string "#(vector %)"))))))
+ (is (not (nil? (re-matches #"\(fn\* \[([\S]+)] \(vector \1 \1\)\)" (pr-str (read-string "#(vector % %)"))))))
+ (is (not (nil? (re-matches #"\(fn\* \[([\S]+)] \(vector \1 \1\)\)" (pr-str (read-string "#(vector % %1)"))))))
+ (is (not (nil? (re-matches #"\(fn\* \[([\S]+) ([\S]+)] \(vector \1 \2\)\)" (pr-str (read-string "#(vector %1 %2)"))))))
+ (is (not (nil? (re-matches #"\(fn\* \[([\S]+) ([\S]+) & ([\S]+)] \(vector \2 \3\)\)" (pr-str (read-string "#(vector %2 %&)"))))))
+
+ ;; invalid formats
+ (is (thrown? RuntimeException (read-string "#(vector %%)")))
+ (is (thrown? RuntimeException (read-string "#(vector %1/2)")))
+ (is (thrown? RuntimeException (read-string "#(vector %1.5)")))
+ (is (thrown? RuntimeException (read-string "#(vector %-0.2)")))
+ (is (thrown? RuntimeException (read-string "#(vector %3M)")))
+
+ ;; check sneaking in discarded content
+ (is (thrown? RuntimeException (read-string "#(list %#_(first arg)1.00000001 %#_(secong arg)2r10 %#_(rest arg)-1.5)")))
+ )
;; Syntax-quote (`, note, the "backquote" character), Unquote (~) and
;; Unquote-splicing (~@)
From 838783effed26b4779454faaa0e953912a6ab74f Mon Sep 17 00:00:00 2001
From: JarrodCTaylor
Date: Fri, 20 Oct 2023 15:12:46 -0500
Subject: [PATCH 185/285] CLJ-1162 Improved error message from invalid deref
---
src/clj/clojure/core.clj | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index d560f67d57..6d8f24cbdd 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -2333,13 +2333,13 @@
value is available. See also - realized?."
{:added "1.0"
:static true}
- ([ref] (if (instance? clojure.lang.IDeref ref)
- (.deref ^clojure.lang.IDeref ref)
- (deref-future ref)))
+ ([ref] (if (instance? java.util.concurrent.Future ref)
+ (deref-future ref)
+ (.deref ^clojure.lang.IDeref ref)))
([ref timeout-ms timeout-val]
- (if (instance? clojure.lang.IBlockingDeref ref)
- (.deref ^clojure.lang.IBlockingDeref ref timeout-ms timeout-val)
- (deref-future ref timeout-ms timeout-val))))
+ (if (instance? java.util.concurrent.Future ref)
+ (deref-future ref timeout-ms timeout-val)
+ (.deref ^clojure.lang.IBlockingDeref ref timeout-ms timeout-val))))
(defn atom
"Creates and returns an Atom with an initial value of x and zero or
From 00bf414696223354ea9d6c5f2e8e8142707f241b Mon Sep 17 00:00:00 2001
From: fogus
Date: Thu, 8 Feb 2024 15:13:24 -0600
Subject: [PATCH 186/285] CLJ-2788: Added bindings around the io-task for
invoke-task to set print bindings to non-truncating values and non-namespaced
maps
---
src/clj/clojure/tools/deps/interop.clj | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/src/clj/clojure/tools/deps/interop.clj b/src/clj/clojure/tools/deps/interop.clj
index 25d0f2548d..0fd5869948 100644
--- a/src/clj/clojure/tools/deps/interop.clj
+++ b/src/clj/clojure/tools/deps/interop.clj
@@ -34,11 +34,14 @@
command-strs [command (str "-T" (or tool-alias tool-name)) "-"]
_ (when (:debug opts) (apply println "Invoking: " command-strs))
{:keys [in out]} (apply proc/start command-strs)]
- (proc/io-task
- #(with-open [w (jio/writer in)]
- (doseq [a args]
- (.write w (pr-str a))
- (.write w " "))))
+ (binding [*print-length* nil
+ *print-level* nil
+ *print-namespace-maps* false]
+ (proc/io-task
+ #(with-open [w (jio/writer in)]
+ (doseq [a args]
+ (.write w (pr-str a))
+ (.write w " ")))))
(let [envelope (edn/read-string (slurp out))]
(if preserve-envelope
envelope
From e58aaef8b6790769b6950b32fbddb8cabb707747 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 8 Feb 2024 15:42:00 -0600
Subject: [PATCH 187/285] Revert "CLJ-2568 clojure.walk now retains metadata
when walking lists and seqs"
This reverts commit 8895fef132bd064032e737e55d54d9ba2c07a0c6.
---
src/clj/clojure/walk.clj | 4 ++--
test/clojure/test_clojure/clojure_walk.clj | 11 -----------
test/clojure/test_clojure/reader.cljc | 5 ++---
3 files changed, 4 insertions(+), 16 deletions(-)
diff --git a/src/clj/clojure/walk.clj b/src/clj/clojure/walk.clj
index 0f027e7ad0..fe8fb631bd 100644
--- a/src/clj/clojure/walk.clj
+++ b/src/clj/clojure/walk.clj
@@ -41,10 +41,10 @@ the sorting function."}
{:added "1.1"}
[inner outer form]
(cond
- (list? form) (outer (with-meta (apply list (map inner form)) (meta form)))
+ (list? form) (outer (apply list (map inner form)))
(instance? clojure.lang.IMapEntry form)
(outer (clojure.lang.MapEntry/create (inner (key form)) (inner (val form))))
- (seq? form) (outer (with-meta (doall (map inner form)) (meta form)))
+ (seq? form) (outer (doall (map inner form)))
(instance? clojure.lang.IRecord form)
(outer (reduce (fn [r x] (conj r (inner x))) form form))
(coll? form) (outer (into (empty form) (map inner form)))
diff --git a/test/clojure/test_clojure/clojure_walk.clj b/test/clojure/test_clojure/clojure_walk.clj
index fa2f3e6492..6c6f866332 100644
--- a/test/clojure/test_clojure/clojure_walk.clj
+++ b/test/clojure/test_clojure/clojure_walk.clj
@@ -63,14 +63,3 @@
(let [coll [:html {:a ["b" 1]} ""]
f (fn [e] (if (and (vector? e) (not (map-entry? e))) (apply list e) e))]
(is (= (list :html {:a (list "b" 1)} "") (w/postwalk f coll)))))
-
-(defrecord RM [a])
-(deftest retain-meta
- (let [m {:foo true}]
- (are [o] (= m (meta (w/postwalk identity (with-meta o m))))
- '(1 2)
- [1 2]
- #{1 2}
- {1 2}
- (map inc (range 3))
- (->RM 1))))
\ No newline at end of file
diff --git a/test/clojure/test_clojure/reader.cljc b/test/clojure/test_clojure/reader.cljc
index a0e1f1f13e..e5fcf5d533 100644
--- a/test/clojure/test_clojure/reader.cljc
+++ b/test/clojure/test_clojure/reader.cljc
@@ -428,9 +428,8 @@
(doseq [form top-levels]
(clojure.walk/postwalk
#(when (list? %)
- (when (contains? expected-metadata (first %))
- (is (= (expected-metadata (first %))
- (meta %))))
+ (is (= (expected-metadata (first %))
+ (meta %)))
(is (->> (meta %)
vals
(filter number?)
From 28c69862bdb9528468f5cbdb684a721cf5b92ad1 Mon Sep 17 00:00:00 2001
From: clojure-build
Date: Thu, 8 Feb 2024 21:53:41 +0000
Subject: [PATCH 188/285] [maven-release-plugin] prepare release
clojure-1.12.0-alpha6
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 2dd37faaad..79049ae7e2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.12.0-master-SNAPSHOT
+ 1.12.0-alpha6
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- HEAD
+ clojure-1.12.0-alpha6
From b56f95197e1dcfacf7c245fb68a10daae1952be2 Mon Sep 17 00:00:00 2001
From: clojure-build
Date: Thu, 8 Feb 2024 21:53:41 +0000
Subject: [PATCH 189/285] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 79049ae7e2..2dd37faaad 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.12.0-alpha6
+ 1.12.0-master-SNAPSHOT
http://clojure.org/
Clojure core environment and runtime library.
@@ -30,7 +30,7 @@
scm:git:git@github.com:clojure/clojure.git
scm:git:git@github.com:clojure/clojure.git
git@github.com:clojure/clojure.git
- clojure-1.12.0-alpha6
+ HEAD
From e3520c07e8b21dbf5a91fa14b7114b90dc1e89c6 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Thu, 8 Feb 2024 19:39:55 -0600
Subject: [PATCH 190/285] Revert "CLJ-2783 Update deprecated process of
creating URLs in Java 20+"
This reverts commit 882d660114c95b9959ee6124a0f1a420d19d8d4f.
---
src/clj/clojure/java/io.clj | 16 +++++++---------
src/clj/clojure/repl/deps.clj | 6 +++---
src/jvm/clojure/lang/RT.java | 18 ++++++------------
test/clojure/test_clojure/java/io.clj | 3 +--
4 files changed, 17 insertions(+), 26 deletions(-)
diff --git a/src/clj/clojure/java/io.clj b/src/clj/clojure/java/io.clj
index 1f6d121bc1..f0715abfd4 100644
--- a/src/clj/clojure/java/io.clj
+++ b/src/clj/clojure/java/io.clj
@@ -12,7 +12,6 @@
clojure.java.io
(:require clojure.string)
(:import
- (clojure.lang RT)
(java.io Reader InputStream InputStreamReader PushbackReader
BufferedReader File OutputStream
OutputStreamWriter BufferedWriter Writer
@@ -20,8 +19,7 @@
StringReader ByteArrayInputStream
BufferedInputStream BufferedOutputStream
CharArrayReader Closeable)
- (java.lang IllegalArgumentException)
- (java.net URI URL Socket URLDecoder URLEncoder)))
+ (java.net URI URL MalformedURLException Socket URLDecoder URLEncoder)))
(set! *warn-on-reflection* true)
@@ -52,11 +50,11 @@
String
(as-file [s] (File. s))
- (as-url [s] (RT/toUrl s))
+ (as-url [s] (URL. s))
File
(as-file [f] f)
- (as-url [f] (RT/toUrl f))
+ (as-url [f] (.toURL (.toURI f)))
URL
(as-url [u] u)
@@ -257,13 +255,13 @@
(assoc default-streams-impl
:make-input-stream (fn [^String x opts]
(try
- (make-input-stream (RT/toUrl x) opts)
- (catch IllegalArgumentException e
+ (make-input-stream (URL. x) opts)
+ (catch MalformedURLException e
(make-input-stream (File. x) opts))))
:make-output-stream (fn [^String x opts]
(try
- (make-output-stream (RT/toUrl x) opts)
- (catch IllegalArgumentException err
+ (make-output-stream (URL. x) opts)
+ (catch MalformedURLException err
(make-output-stream (File. x) opts))))))
(extend Socket
diff --git a/src/clj/clojure/repl/deps.clj b/src/clj/clojure/repl/deps.clj
index dfd3137fcd..1ed8365a1c 100644
--- a/src/clj/clojure/repl/deps.clj
+++ b/src/clj/clojure/repl/deps.clj
@@ -14,7 +14,7 @@
[clojure.java.basis.impl :as basis-impl]
[clojure.tools.deps.interop :as tool])
(:import
- [clojure.lang DynamicClassLoader RT]
+ [clojure.lang DynamicClassLoader]
[java.io File]))
(set! *warn-on-reflection* true)
@@ -22,7 +22,7 @@
(defn- add-loader-url
"Add url string or URL to the highest level DynamicClassLoader url set."
[url]
- (let [u (if (string? url) (RT/toUrl ^String url) url)
+ (let [u (if (string? url) (java.net.URL. url) url)
loader (loop [loader (.getContextClassLoader (Thread/currentThread))]
(let [parent (.getParent loader)]
(if (instance? DynamicClassLoader parent)
@@ -48,7 +48,7 @@
{:keys [added] :as _res} (tool/invoke-tool {:tool-alias :deps, :fn 'clojure.tools.deps/resolve-added-libs, :args tool-args})
;_ (clojure.pprint/pprint _res)
paths (mapcat :paths (vals added))
- urls (->> paths (map jio/file) (map #(RT/toUrl ^File %)))]
+ urls (->> paths (map jio/file) (map #(.toURL ^File %)))]
(run! add-loader-url urls)
(basis-impl/update-basis! update :libs merge added)
(let [ret (-> added keys sort vec)]
diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java
index 0ff3e50a2b..86c89d69fb 100644
--- a/src/jvm/clojure/lang/RT.java
+++ b/src/jvm/clojure/lang/RT.java
@@ -12,7 +12,7 @@
package clojure.lang;
-import java.net.*;
+import java.net.MalformedURLException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.Callable;
import java.util.*;
@@ -20,12 +20,14 @@
import java.util.regex.Pattern;
import java.io.*;
import java.lang.reflect.Array;
-import java.lang.IllegalArgumentException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.AccessController;
import java.security.PrivilegedAction;
+import java.net.URL;
+import java.net.JarURLConnection;
import java.nio.charset.Charset;
+import java.net.URLConnection;
public class RT{
@@ -291,16 +293,8 @@ private Object readResolve() throws ObjectStreamException {
static AtomicInteger id = new AtomicInteger(1);
-static public URL toUrl(String url) throws URISyntaxException, MalformedURLException {
- return new URI(url).toURL();
-}
-
-static public URL toUrl(File file) throws MalformedURLException {
- return file.toURI().toURL();
-}
-
-static public void addURL(Object url) throws MalformedURLException, URISyntaxException, IllegalArgumentException {
- URL u = (url instanceof String) ? toUrl((String) url) : (URL) url;
+static public void addURL(Object url) throws MalformedURLException{
+ URL u = (url instanceof String) ? (new URL((String) url)) : (URL) url;
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if(ccl instanceof DynamicClassLoader)
((DynamicClassLoader)ccl).addURL(u);
diff --git a/test/clojure/test_clojure/java/io.clj b/test/clojure/test_clojure/java/io.clj
index fb179e96da..b83c63d311 100644
--- a/test/clojure/test_clojure/java/io.clj
+++ b/test/clojure/test_clojure/java/io.clj
@@ -13,7 +13,6 @@
FileInputStream InputStreamReader InputStream
FileOutputStream OutputStreamWriter OutputStream
ByteArrayInputStream ByteArrayOutputStream)
- (clojure.lang RT)
(java.net URL URI Socket ServerSocket)))
(defn temp-file
@@ -49,7 +48,7 @@
(slurp read-from :encoding "UTF-8")))
f f
(.getAbsolutePath f) (.getAbsolutePath f)
- (RT/toUrl f) (RT/toUrl f)
+ (.toURL f) (.toURL f)
(.toURI f) (.toURI f)
(FileOutputStream. f) (FileInputStream. f)
(OutputStreamWriter. (FileOutputStream. f) "UTF-8") (reader f :encoding "UTF-8")
From 64382fe41573b0faf296129e6c4b626535c2eda6 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Wed, 21 Feb 2024 10:27:50 -0600
Subject: [PATCH 191/285] Revert "CLJ-1162 Improved error message from invalid
deref"
This reverts commit 838783effed26b4779454faaa0e953912a6ab74f.
---
src/clj/clojure/core.clj | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 6d8f24cbdd..d560f67d57 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -2333,13 +2333,13 @@
value is available. See also - realized?."
{:added "1.0"
:static true}
- ([ref] (if (instance? java.util.concurrent.Future ref)
- (deref-future ref)
- (.deref ^clojure.lang.IDeref ref)))
+ ([ref] (if (instance? clojure.lang.IDeref ref)
+ (.deref ^clojure.lang.IDeref ref)
+ (deref-future ref)))
([ref timeout-ms timeout-val]
- (if (instance? java.util.concurrent.Future ref)
- (deref-future ref timeout-ms timeout-val)
- (.deref ^clojure.lang.IBlockingDeref ref timeout-ms timeout-val))))
+ (if (instance? clojure.lang.IBlockingDeref ref)
+ (.deref ^clojure.lang.IBlockingDeref ref timeout-ms timeout-val)
+ (deref-future ref timeout-ms timeout-val))))
(defn atom
"Creates and returns an Atom with an initial value of x and zero or
From 089f27f9fc881674e565f1ffcbe2f8a616726c3b Mon Sep 17 00:00:00 2001
From: JarrodCTaylor
Date: Tue, 13 Feb 2024 14:04:08 -0600
Subject: [PATCH 192/285] CLJ-2783 Update deprecated process of creating URLs
in Java 20+
---
src/clj/clojure/java/io.clj | 9 +++++----
src/clj/clojure/repl/deps.clj | 6 +++---
src/jvm/clojure/lang/RT.java | 21 ++++++++++++++++++++-
test/clojure/test_clojure/java/io.clj | 6 +++++-
4 files changed, 33 insertions(+), 9 deletions(-)
diff --git a/src/clj/clojure/java/io.clj b/src/clj/clojure/java/io.clj
index f0715abfd4..5b96664e01 100644
--- a/src/clj/clojure/java/io.clj
+++ b/src/clj/clojure/java/io.clj
@@ -19,6 +19,7 @@
StringReader ByteArrayInputStream
BufferedInputStream BufferedOutputStream
CharArrayReader Closeable)
+ (clojure.lang RT)
(java.net URI URL MalformedURLException Socket URLDecoder URLEncoder)))
(set! *warn-on-reflection* true)
@@ -50,11 +51,11 @@
String
(as-file [s] (File. s))
- (as-url [s] (URL. s))
+ (as-url [s] (RT/toUrl s))
File
(as-file [f] f)
- (as-url [f] (.toURL (.toURI f)))
+ (as-url [f] (RT/toUrl f))
URL
(as-url [u] u)
@@ -255,12 +256,12 @@
(assoc default-streams-impl
:make-input-stream (fn [^String x opts]
(try
- (make-input-stream (URL. x) opts)
+ (make-input-stream (RT/toUrl x) opts)
(catch MalformedURLException e
(make-input-stream (File. x) opts))))
:make-output-stream (fn [^String x opts]
(try
- (make-output-stream (URL. x) opts)
+ (make-output-stream (RT/toUrl x) opts)
(catch MalformedURLException err
(make-output-stream (File. x) opts))))))
diff --git a/src/clj/clojure/repl/deps.clj b/src/clj/clojure/repl/deps.clj
index 1ed8365a1c..dfd3137fcd 100644
--- a/src/clj/clojure/repl/deps.clj
+++ b/src/clj/clojure/repl/deps.clj
@@ -14,7 +14,7 @@
[clojure.java.basis.impl :as basis-impl]
[clojure.tools.deps.interop :as tool])
(:import
- [clojure.lang DynamicClassLoader]
+ [clojure.lang DynamicClassLoader RT]
[java.io File]))
(set! *warn-on-reflection* true)
@@ -22,7 +22,7 @@
(defn- add-loader-url
"Add url string or URL to the highest level DynamicClassLoader url set."
[url]
- (let [u (if (string? url) (java.net.URL. url) url)
+ (let [u (if (string? url) (RT/toUrl ^String url) url)
loader (loop [loader (.getContextClassLoader (Thread/currentThread))]
(let [parent (.getParent loader)]
(if (instance? DynamicClassLoader parent)
@@ -48,7 +48,7 @@
{:keys [added] :as _res} (tool/invoke-tool {:tool-alias :deps, :fn 'clojure.tools.deps/resolve-added-libs, :args tool-args})
;_ (clojure.pprint/pprint _res)
paths (mapcat :paths (vals added))
- urls (->> paths (map jio/file) (map #(.toURL ^File %)))]
+ urls (->> paths (map jio/file) (map #(RT/toUrl ^File %)))]
(run! add-loader-url urls)
(basis-impl/update-basis! update :libs merge added)
(let [ret (-> added keys sort vec)]
diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java
index 86c89d69fb..d56023a063 100644
--- a/src/jvm/clojure/lang/RT.java
+++ b/src/jvm/clojure/lang/RT.java
@@ -13,6 +13,8 @@
package clojure.lang;
import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URI;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.Callable;
import java.util.*;
@@ -20,6 +22,7 @@
import java.util.regex.Pattern;
import java.io.*;
import java.lang.reflect.Array;
+import java.lang.IllegalArgumentException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.AccessController;
@@ -293,8 +296,24 @@ private Object readResolve() throws ObjectStreamException {
static AtomicInteger id = new AtomicInteger(1);
+static public URL toUrl(String url) throws MalformedURLException {
+ try {
+ return new URI(url).toURL();
+ } catch (URISyntaxException | IllegalArgumentException e) {
+ // Maintain backward compatibility with error
+ // thrown from now deprecated java.net.URL
+ MalformedURLException ex = new MalformedURLException();
+ ex.initCause(e);
+ throw ex;
+ }
+}
+
+static public URL toUrl(File file) throws MalformedURLException {
+ return file.toURI().toURL();
+}
+
static public void addURL(Object url) throws MalformedURLException{
- URL u = (url instanceof String) ? (new URL((String) url)) : (URL) url;
+ URL u = (url instanceof String) ? toUrl((String) url) : (URL) url;
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if(ccl instanceof DynamicClassLoader)
((DynamicClassLoader)ccl).addURL(u);
diff --git a/test/clojure/test_clojure/java/io.clj b/test/clojure/test_clojure/java/io.clj
index b83c63d311..78864220d6 100644
--- a/test/clojure/test_clojure/java/io.clj
+++ b/test/clojure/test_clojure/java/io.clj
@@ -13,6 +13,7 @@
FileInputStream InputStreamReader InputStream
FileOutputStream OutputStreamWriter OutputStream
ByteArrayInputStream ByteArrayOutputStream)
+ (clojure.lang RT)
(java.net URL URI Socket ServerSocket)))
(defn temp-file
@@ -48,7 +49,7 @@
(slurp read-from :encoding "UTF-8")))
f f
(.getAbsolutePath f) (.getAbsolutePath f)
- (.toURL f) (.toURL f)
+ (RT/toUrl f) (RT/toUrl f)
(.toURI f) (.toURI f)
(FileOutputStream. f) (FileInputStream. f)
(OutputStreamWriter. (FileOutputStream. f) "UTF-8") (reader f :encoding "UTF-8")
@@ -60,6 +61,9 @@
(finally
(.delete f)))))
+(deftest clj-2783-slurp-maintains-backward-compatibility-errors
+ (is (thrown? java.io.FileNotFoundException (slurp "whitespace = #'\\s+'"))))
+
(deftest test-streams-nil
(is (thrown-with-msg? IllegalArgumentException #"Cannot open.*nil" (reader nil)))
(is (thrown-with-msg? IllegalArgumentException #"Cannot open.*nil" (writer nil)))
From 4dbdcb1fe8128620162839a60192d899eb3c83f7 Mon Sep 17 00:00:00 2001
From: Alex Miller
Date: Tue, 13 Feb 2024 15:38:02 -0600
Subject: [PATCH 193/285] CLJ-2568 clojure.walk now retains metadata when
walking lists and seqs
---
src/clj/clojure/walk.clj | 4 ++--
test/clojure/test_clojure/clojure_walk.clj | 11 +++++++++++
test/clojure/test_clojure/printer.clj | 5 +++--
test/clojure/test_clojure/reader.cljc | 5 +++--
4 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/src/clj/clojure/walk.clj b/src/clj/clojure/walk.clj
index fe8fb631bd..0f027e7ad0 100644
--- a/src/clj/clojure/walk.clj
+++ b/src/clj/clojure/walk.clj
@@ -41,10 +41,10 @@ the sorting function."}
{:added "1.1"}
[inner outer form]
(cond
- (list? form) (outer (apply list (map inner form)))
+ (list? form) (outer (with-meta (apply list (map inner form)) (meta form)))
(instance? clojure.lang.IMapEntry form)
(outer (clojure.lang.MapEntry/create (inner (key form)) (inner (val form))))
- (seq? form) (outer (doall (map inner form)))
+ (seq? form) (outer (with-meta (doall (map inner form)) (meta form)))
(instance? clojure.lang.IRecord form)
(outer (reduce (fn [r x] (conj r (inner x))) form form))
(coll? form) (outer (into (empty form) (map inner form)))
diff --git a/test/clojure/test_clojure/clojure_walk.clj b/test/clojure/test_clojure/clojure_walk.clj
index 6c6f866332..fa2f3e6492 100644
--- a/test/clojure/test_clojure/clojure_walk.clj
+++ b/test/clojure/test_clojure/clojure_walk.clj
@@ -63,3 +63,14 @@
(let [coll [:html {:a ["b" 1]} ""]
f (fn [e] (if (and (vector? e) (not (map-entry? e))) (apply list e) e))]
(is (= (list :html {:a (list "b" 1)} "") (w/postwalk f coll)))))
+
+(defrecord RM [a])
+(deftest retain-meta
+ (let [m {:foo true}]
+ (are [o] (= m (meta (w/postwalk identity (with-meta o m))))
+ '(1 2)
+ [1 2]
+ #{1 2}
+ {1 2}
+ (map inc (range 3))
+ (->RM 1))))
\ No newline at end of file
diff --git a/test/clojure/test_clojure/printer.clj b/test/clojure/test_clojure/printer.clj
index ffb3007138..0216f4f48f 100644
--- a/test/clojure/test_clojure/printer.clj
+++ b/test/clojure/test_clojure/printer.clj
@@ -123,8 +123,9 @@
(deftest print-throwable
(binding [*data-readers* {'error identity}]
- (are [e] (= (-> e Throwable->map)
- (-> e pr-str read-string))
+ (are [e] (let [e' e] ;; use same templated exception object in both cases
+ (= (-> e' Throwable->map)
+ (-> e' pr-str read-string)))
(Exception. "heyo")
(Throwable. "I can a throwable"
(Exception. "chain 1"
diff --git a/test/clojure/test_clojure/reader.cljc b/test/clojure/test_clojure/reader.cljc
index e5fcf5d533..a0e1f1f13e 100644
--- a/test/clojure/test_clojure/reader.cljc
+++ b/test/clojure/test_clojure/reader.cljc
@@ -428,8 +428,9 @@
(doseq [form top-levels]
(clojure.walk/postwalk
#(when (list? %)
- (is (= (expected-metadata (first %))
- (meta %)))
+ (when (contains? expected-metadata (first %))
+ (is (= (expected-metadata (first %))
+ (meta %))))
(is (->> (meta %)
vals
(filter number?)
From 6355ed6fa62a38604291fe1f83c27eaa7ee6e45b Mon Sep 17 00:00:00 2001
From: clojure-build