diff --git a/build.xml b/build.xml
index cae30b21..35f4b28f 100644
--- a/build.xml
+++ b/build.xml
@@ -92,7 +92,7 @@
debug="true" source="1.6" target="1.6" includeantruntime="no"/>
Direct linking = ${directlinking}
@@ -194,4 +194,11 @@
+
+
+
+
+
+
+
diff --git a/changes.md b/changes.md
index 61b2eaf8..d387eb5e 100644
--- a/changes.md
+++ b/changes.md
@@ -1,5 +1,181 @@
+# Changes to Clojure in Version 1.9
+
+## 1 New and Improved Features
+
+### 1.1 spec
+
+spec is a new core library for describing, validating, and testing the structure of data and functions.
+
+For more information, see:
+
+* [About spec](https://clojure.org/about/spec)
+* [spec Guide](https://clojure.org/guides/spec)
+
+Note that spec is in alpha state and API compatibility is not guaranteed. Also, spec and the specs for the Clojure core API are distributed as external libraries that must be included to use Clojure.
+
+### 1.2 Support for working with maps with qualified keys
+
+Several enhancements have been made to add support for working with maps with qualified keys:
+
+* Map namespace syntax - specify the default namespace context for the keys (or symbols) in a map once - `#:car{:make "Jeep" :model "Wrangler"}`. For more information see https://clojure.org/reference/reader#_maps ([CLJ-1910](http://dev.clojure.org/jira/browse/CLJ-1910))
+* Destructuring support - namespaced map keys can now specified once as a namespace for :keys or :syms. For more information see https://clojure.org/reference/special_forms#_map_binding_destructuring ([CLJ-1919](http://dev.clojure.org/jira/browse/CLJ-1919))
+* `*print-namespace-maps*` - by default maps will not print with the map namespace syntax except in the clojure.main repl. This dynamic var is a flag to allow you to control whether the namespace map syntax is used.
+
+### 1.3 New predicates
+
+Specs rely heavily on predicates and many new type and value oriented predicates have been added to clojure.core:
+
+* `boolean?`
+* `int?` `pos-int?` `neg-int?` `nat-int?`
+* `double?`
+* `ident?` `simple-ident?` `qualified-ident?`
+* `simple-symbol?` `qualified-symbol?`
+* `simple-keyword?` `qualified-keyword?`
+* `bytes?` (for `byte[]`)
+* `indexed?`
+* `uuid?` `uri?`
+* `seqable?`
+* `any?`
+
+### 1.4 More support for instants
+
+More support has been added for the notion of instants in time:
+
+* Added a new protocol `Inst` for instant types
+* `Inst` is extended for `java.util.Date`
+* `Inst` is optionally extended for `java.time.Instant` in Java 1.8+
+* New functions that work for instants: `inst?`, `inst-ms`
+
+### 1.5 Other new core functions
+
+These are some other new functions in clojure.core:
+
+* `bounded-count` - a count that avoids realizing the entire collection beyond a bound
+* `swap-vals!` and `reset-vals!` - new atom functions that return both the old and new values ([CLJ-1454](http://dev.clojure.org/jira/browse/CLJ-1454))
+* `halt-when` - new transducer that ends transduction when pred is satisfied
+
+### 1.6 Other reader enhancements
+
+* Can now bind `*reader-resolver*` to an impl of LispReader$Resolver to control the reader’s use of namespace interactions when resolving autoresolved keywords and maps.
+* Add new ## reader macro for symbolic values, and read/print support for double vals ##Inf, ##-Inf, ##NaN ([CLJ-1074](http://dev.clojure.org/jira/browse/CLJ-1074))
+
+## 2 Enhancements
+
+### 2.1 Spec syntax checking
+
+If a macro has a spec defined via fdef, that spec will be checked at compile time. Specs have been defined for many clojure.core macros and errors will be reported for these based on the specs at compile time.
+
+### 2.2 Documentation
+
+* `doc` will now report specs for functions with specs defined using `fdef`
+* `doc` can now be invoked with a fully-qualified keyword representing a spec name
+
+### 2.3 Performance
+
+* Improved update-in performance
+* Optimized seq & destructuring
+* [CLJ-2210](http://dev.clojure.org/jira/browse/CLJ-2210)
+ Cache class derivation in compiler to improve compiler performance
+* [CLJ-2188](http://dev.clojure.org/jira/browse/CLJ-2188)
+ `slurp` - mark return type as String
+* [CLJ-2070](http://dev.clojure.org/jira/browse/CLJ-2070)
+ `clojure.core/delay` - improve performance
+* [CLJ-1917](http://dev.clojure.org/jira/browse/CLJ-1917)
+ Reducing seq over string should call String/length outside of loop
+* [CLJ-1901](http://dev.clojure.org/jira/browse/CLJ-1901)
+ `amap` - should call alength only once
+* [CLJ-1224](http://dev.clojure.org/jira/browse/CLJ-1935)
+ Record instances now cache hasheq and hashCode like maps
+* [CLJ-99](http://dev.clojure.org/jira/browse/CLJ-99)
+ `min-key` and `max-key` - evaluate k on each arg at most once
+
+### 2.4 Other enhancements
+
+* Added Var serialization for identity, not value
+* `into` now has a 0-arity (returns `[]`) and 1-arity (returns the coll that's passed)
+* [CLJ-2184](http://dev.clojure.org/jira/browse/CLJ-2184)
+ Propagate meta in doto forms to improve error reporting
+* [CLJ-1744](http://dev.clojure.org/jira/browse/CLJ-1744)
+ Clear unused locals, which can prevent memory leaks in some cases
+* [CLJ-1673](http://dev.clojure.org/jira/browse/CLJ-1673)
+ `clojure.repl/dir-fn` now works on namespace aliases
+* [CLJ-1423](http://dev.clojure.org/jira/browse/CLJ-1423)
+ Allow vars to be invoked with infinite arglists (also, faster)
+
+## 3 Fixes
+
+### 3.1 Security
+
+* [CLJ-2204](http://dev.clojure.org/jira/browse/CLJ-2204)
+ Disable serialization of proxy classes to avoid potential issue when deserializing
+
+### 3.2 Docs
+
+* [CLJ-2170](http://dev.clojure.org/jira/browse/CLJ-2170)
+ fix improperly located docstrings
+* [CLJ-2156](http://dev.clojure.org/jira/browse/CLJ-2156)
+ `clojure.java.io/copy` - doc char[] support
+* [CLJ-2104](http://dev.clojure.org/jira/browse/CLJ-2104)
+ `clojure.pprint` docstring - fix typo
+* [CLJ-2051](http://dev.clojure.org/jira/browse/CLJ-2051)
+ `clojure.instant/validated` docstring - fix typo
+* [CLJ-2039](http://dev.clojure.org/jira/browse/CLJ-2039)
+ `deftype` - fix typo in docstring
+* [CLJ-2028](http://dev.clojure.org/jira/browse/CLJ-2028)
+ `filter`, `filterv`, `remove`, `take-while` - fix docstrings
+* [CLJ-1918](http://dev.clojure.org/jira/browse/CLJ-1918)
+ `await` - improve docstring re `shutdown-agents`
+* [CLJ-1873](http://dev.clojure.org/jira/browse/CLJ-1873)
+ `require`, `*data-readers*` - add .cljc files to docstrings
+* [CLJ-1859](http://dev.clojure.org/jira/browse/CLJ-1859)
+ `zero?`, `pos?`, `neg?` - fix docstrings
+* [CLJ-1837](http://dev.clojure.org/jira/browse/CLJ-1837)
+ `index-of`, `last-index-of` - clarify docstrings
+* [CLJ-1826](http://dev.clojure.org/jira/browse/CLJ-1826)
+ `drop-last` - fix docstring
+* [CLJ-1159](http://dev.clojure.org/jira/browse/CLJ-1159)
+ `clojure.java.io/delete-file` - improve docstring
+
+### 3.3 Other fixes
+
+* `clojure.core/Throwable->map` formerly returned `StackTraceElement`s which were later handled by the printer. Now the StackTraceElements are converted to data such that the return value is pure Clojure data, as intended.
+* [CLJ-2091](http://dev.clojure.org/jira/browse/CLJ-2091)
+ `clojure.lang.APersistentVector#hashCode` is not thread-safe
+* [CLJ-2077](http://dev.clojure.org/jira/browse/CLJ-2077)
+ Clojure can't be loaded from the boot classpath under java 9
+* [CLJ-2048](http://dev.clojure.org/jira/browse/CLJ-2048)
+ Specify type to avoid ClassCastException when stack trace is elided by JVM
+* [CLJ-1914](http://dev.clojure.org/jira/browse/CLJ-1914)
+ Fixed race condition in concurrent `range` realization
+* [CLJ-1887](http://dev.clojure.org/jira/browse/CLJ-1887)
+ `IPersistentVector.length()` - implement missing method
+* [CLJ-1870](http://dev.clojure.org/jira/browse/CLJ-1870)
+ Fixed reloading a `defmulti` removes metadata on the var
+* [CLJ-1860](http://dev.clojure.org/jira/browse/CLJ-1860)
+ Make -0.0 hash consistent with 0.0
+* [CLJ-1841](http://dev.clojure.org/jira/browse/CLJ-1841)
+ `bean` - iterator was broken
+* [CLJ-1793](http://dev.clojure.org/jira/browse/CLJ-1793)
+ Clear 'this' before calls in tail position
+* [CLJ-1790](http://dev.clojure.org/jira/browse/CLJ-1790)
+ Fixed error extending protocols to Java arrays
+* [CLJ-1714](http://dev.clojure.org/jira/browse/CLJ-1714)
+ using a class in a type hint shouldn’t load the class
+* [CLJ-1705](http://dev.clojure.org/jira/browse/CLJ-1705)
+ `vector-of` - fix NullPointerException if given unrecognized type
+* [CLJ-1398](http://dev.clojure.org/jira/browse/CLJ-1398)
+ `clojure.java.javadoc/javadoc` - update doc urls
+* [CLJ-1371](http://dev.clojure.org/jira/browse/CLJ-1371)
+ `Numbers.divide(Object, Object)` - add checks for NaN
+* [CLJ-1358](http://dev.clojure.org/jira/browse/CLJ-1358)
+ `doc` - does not expand special cases properly (try, catch)
+* [CLJ-1242](http://dev.clojure.org/jira/browse/CLJ-1242)
+ equals doesn't throw on sorted collections
+* [CLJ-700](http://dev.clojure.org/jira/browse/CLJ-700)
+ `contains?`, `get`, and `find` broken for transient collections
+
# Changes to Clojure in Version 1.8
## 1 New and Improved Features
diff --git a/pom.xml b/pom.xml
index 482b4c20..f6d5d34c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
clojure
clojure
jar
- 1.8.0
+ 1.9.0
http://clojure.org/
Clojure core environment and runtime library.
@@ -26,16 +26,11 @@
-
- org.sonatype.oss
- oss-parent
- 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.9.0
@@ -43,6 +38,16 @@
+
+ org.clojure
+ spec.alpha
+ 0.1.143
+
+
+ org.clojure
+ core.specs.alpha
+ 0.1.24
+
org.codehaus.jsr166-mirror
jsr166y
@@ -64,7 +69,7 @@
org.clojure
test.check
- 0.5.9
+ 0.9.0
test
@@ -75,6 +80,14 @@
+
+
+
+ sonatype-nexus-staging
+ https://oss.sonatype.org/content/repositories/snapshots
+
+
+
@@ -90,11 +103,11 @@
org.apache.maven.plugins
maven-compiler-plugin
- 2.3.2
+ 3.1
1.6
1.6
- ${project.build.sourceEncoding}
+ UTF-8
@@ -201,16 +214,16 @@
-
- org.apache.maven.plugins
- maven-release-plugin
- 2.1
-
- false
- true
-
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.4.1
+
+ false
+ true
+
@@ -221,6 +234,22 @@
true
+
+
+
+
+
+
@@ -266,39 +295,64 @@
- sonatype-oss-release
-
+
+ sign
org.apache.maven.plugins
- maven-deploy-plugin
- 2.7
-
- true
-
+ maven-gpg-plugin
+ 1.5
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+
+
+ local
+
+
+ org.clojure
+ test.check
+ 0.9.0
+
+
+ org.clojure
+ clojure
+
+
+
+
+
+
- org.sonatype.plugins
- nexus-staging-maven-plugin
- 1.4.4
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.1.0
- default-deploy
- deploy
-
+ package
- deploy
+ shade
+
+
+
+ clojure.main
+
+
+ clojure.jar
+
-
-
- https://oss.sonatype.org/
-
- sonatype-nexus-staging
-
diff --git a/readme.txt b/readme.txt
index 4871d0f1..70ee0758 100644
--- a/readme.txt
+++ b/readme.txt
@@ -7,29 +7,32 @@
* the terms of this license.
* You must not remove this notice, or any other, from this software.
-Docs: http://clojure.org
+Docs: https://clojure.org
Feedback: http://groups.google.com/group/clojure
-Getting Started: http://dev.clojure.org/display/doc/Getting+Started
+Getting Started: https://clojure.org/guides/getting_started
-To run: java -cp clojure-${VERSION}.jar clojure.main
-
-To build locally with Ant:
+To build and run locally with Ant:
One-time setup: ./antsetup.sh
- To build: ant
+ To build: ant local
+ To run: java -jar clojure.jar
-Maven 2 build instructions:
+To build locally with Maven:
- To build: mvn package
- The built JARs will be in target/
+ To build (output JARs in target/):
+ mvn package
- To build without testing: mvn package -Dmaven.test.skip=true
+ To build without testing:
+ mvn package -Dmaven.test.skip=true
- To build and install in local Maven repository: mvn install
+ To build and install in local Maven repository:
+ mvn install
- To build a ZIP distribution: mvn package -Pdistribution
- The built .zip will be in target/
+ To build a standalone jar with dependencies included:
+ mvn -Plocal -Dmaven.test.skip=true package
+ To run with the standalone jar:
+ java -jar clojure.jar
--------------------------------------------------------------------------
This program uses the ASM bytecode engineering library which is distributed
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 2e128a5c..c5f65927 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -516,6 +516,11 @@
:static true}
[x] (clojure.lang.Util/identical x true))
+(defn boolean?
+ "Return true if x is a Boolean"
+ {:added "1.9"}
+ [x] (instance? Boolean x))
+
(defn not
"Returns true if x is logical false, false otherwise."
{:tag Boolean
@@ -530,6 +535,12 @@
:static true}
[x] (not (nil? x)))
+(defn any?
+ "Returns true given any argument."
+ {:tag Boolean
+ :added "1.9"}
+ [x] true)
+
(defn str
"With no args, returns the empty string. With one arg x, returns
x.toString(). (str nil) returns the empty string. With more than
@@ -848,9 +859,9 @@
(defn zero?
"Returns true if num is zero, else false"
{
- :inline (fn [x] `(. clojure.lang.Numbers (isZero ~x)))
+ :inline (fn [num] `(. clojure.lang.Numbers (isZero ~num)))
:added "1.0"}
- [x] (. clojure.lang.Numbers (isZero x)))
+ [num] (. clojure.lang.Numbers (isZero num)))
(defn count
"Returns the number of items in the collection. (count nil) returns
@@ -1228,16 +1239,16 @@
(defn pos?
"Returns true if num is greater than zero, else false"
{
- :inline (fn [x] `(. clojure.lang.Numbers (isPos ~x)))
+ :inline (fn [num] `(. clojure.lang.Numbers (isPos ~num)))
:added "1.0"}
- [x] (. clojure.lang.Numbers (isPos x)))
+ [num] (. clojure.lang.Numbers (isPos num)))
(defn neg?
"Returns true if num is less than zero, else false"
{
- :inline (fn [x] `(. clojure.lang.Numbers (isNeg ~x)))
+ :inline (fn [num] `(. clojure.lang.Numbers (isNeg ~num)))
:added "1.0"}
- [x] (. clojure.lang.Numbers (isNeg x)))
+ [num] (. clojure.lang.Numbers (isNeg num)))
(defn quot
"quot[ient] of dividing numerator by denominator."
@@ -1378,6 +1389,36 @@
:static true}
[n] (not (even? n)))
+(defn int?
+ "Return true if x is a fixed precision integer"
+ {:added "1.9"}
+ [x] (or (instance? Long x)
+ (instance? Integer x)
+ (instance? Short x)
+ (instance? Byte x)))
+
+(defn pos-int?
+ "Return true if x is a positive fixed precision integer"
+ {:added "1.9"}
+ [x] (and (int? x)
+ (pos? x)))
+
+(defn neg-int?
+ "Return true if x is a negative fixed precision integer"
+ {:added "1.9"}
+ [x] (and (int? x)
+ (neg? x)))
+
+(defn nat-int?
+ "Return true if x is a non-negative fixed precision integer"
+ {:added "1.9"}
+ [x] (and (int? x)
+ (not (neg? x))))
+
+(defn double?
+ "Return true if x is a Double"
+ {:added "1.9"}
+ [x] (instance? Double x))
;;
@@ -1553,6 +1594,48 @@
[^clojure.lang.Named x]
(. x (getNamespace)))
+(defn boolean
+ "Coerce to boolean"
+ {
+ :inline (fn [x] `(. clojure.lang.RT (booleanCast ~x)))
+ :added "1.0"}
+ [x] (clojure.lang.RT/booleanCast x))
+
+(defn ident?
+ "Return true if x is a symbol or keyword"
+ {:added "1.9"}
+ [x] (or (keyword? x) (symbol? x)))
+
+(defn simple-ident?
+ "Return true if x is a symbol or keyword without a namespace"
+ {:added "1.9"}
+ [x] (and (ident? x) (nil? (namespace x))))
+
+(defn qualified-ident?
+ "Return true if x is a symbol or keyword with a namespace"
+ {:added "1.9"}
+ [x] (boolean (and (ident? x) (namespace x) true)))
+
+(defn simple-symbol?
+ "Return true if x is a symbol without a namespace"
+ {:added "1.9"}
+ [x] (and (symbol? x) (nil? (namespace x))))
+
+(defn qualified-symbol?
+ "Return true if x is a symbol with a namespace"
+ {:added "1.9"}
+ [x] (boolean (and (symbol? x) (namespace x) true)))
+
+(defn simple-keyword?
+ "Return true if x is a keyword without a namespace"
+ {:added "1.9"}
+ [x] (and (keyword? x) (nil? (namespace x))))
+
+(defn qualified-keyword?
+ "Return true if x is a keyword with a namespace"
+ {:added "1.9"}
+ [x] (boolean (and (keyword? x) (namespace x) true)))
+
(defmacro locking
"Executes exprs in an implicit do, while holding the monitor of x.
Will release the monitor of x in all circumstances."
@@ -1676,7 +1759,8 @@
m)
m (if (meta mm-name)
(conj (meta mm-name) m)
- m)]
+ m)
+ mm-name (with-meta mm-name m)]
(when (= (count options) 1)
(throw (Exception. "The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value)")))
(let [options (apply hash-map options)
@@ -1685,7 +1769,7 @@
(check-valid-options options :default :hierarchy)
`(let [v# (def ~mm-name)]
(when-not (and (.hasRoot v#) (instance? clojure.lang.MultiFn (deref v#)))
- (def ~(with-meta mm-name m)
+ (def ~mm-name
(new clojure.lang.MultiFn ~(name mm-name) ~dispatch-fn ~default ~hierarchy)))))))
(defmacro defmethod
@@ -2277,6 +2361,17 @@
([^clojure.lang.IAtom atom f x y] (.swap atom f x y))
([^clojure.lang.IAtom atom f x y & args] (.swap atom f x y args)))
+(defn swap-vals!
+ "Atomically swaps the value of atom to be:
+ (apply f current-value-of-atom args). Note that f may be called
+ multiple times, and thus should be free of side effects.
+ Returns [old new], the value of the atom before and after the swap."
+ {:added "1.9"}
+ (^clojure.lang.IPersistentVector [^clojure.lang.IAtom2 atom f] (.swapVals atom f))
+ (^clojure.lang.IPersistentVector [^clojure.lang.IAtom2 atom f x] (.swapVals atom f x))
+ (^clojure.lang.IPersistentVector [^clojure.lang.IAtom2 atom f x y] (.swapVals atom f x y))
+ (^clojure.lang.IPersistentVector [^clojure.lang.IAtom2 atom f x y & args] (.swapVals atom f x y args)))
+
(defn compare-and-set!
"Atomically sets the value of atom to newval if and only if the
current value of the atom is identical to oldval. Returns true if
@@ -2292,6 +2387,12 @@
:static true}
[^clojure.lang.IAtom atom newval] (.reset atom newval))
+(defn reset-vals!
+ "Sets the value of atom to newval. Returns [old new], the value of the
+ atom before and after the reset."
+ {:added "1.9"}
+ ^clojure.lang.IPersistentVector [^clojure.lang.IAtom2 atom newval] (.resetVals atom newval))
+
(defn set-validator!
"Sets the validator-fn for a var/ref/agent/atom. validator-fn must be nil or a
side-effect-free fn of one argument, which will be passed the intended
@@ -2698,7 +2799,7 @@
(defn filter
"Returns a lazy sequence of the items in coll for which
- (pred item) returns true. pred must be free of side-effects.
+ (pred item) returns logical true. pred must be free of side-effects.
Returns a transducer when no collection is provided."
{:added "1.0"
:static true}
@@ -2731,7 +2832,7 @@
(defn remove
"Returns a lazy sequence of the items in coll for which
- (pred item) returns false. pred must be free of side-effects.
+ (pred item) returns logical false. pred must be free of side-effects.
Returns a transducer when no collection is provided."
{:added "1.0"
:static true}
@@ -2793,7 +2894,7 @@
(defn take-while
"Returns a lazy sequence of successive items from coll while
- (pred item) returns true. pred must be free of side-effects.
+ (pred item) returns logical true. pred must be free of side-effects.
Returns a transducer when no collection is provided."
{:added "1.0"
:static true}
@@ -2841,8 +2942,8 @@
"Return a lazy sequence of all but the last n (default 1) items in coll"
{:added "1.0"
:static true}
- ([s] (drop-last 1 s))
- ([n s] (map (fn [x _] x) s (drop n s))))
+ ([coll] (drop-last 1 coll))
+ ([n coll] (map (fn [x _] x) coll (drop n coll))))
(defn take-last
"Returns a seq of the last n items in coll. Depending on the type
@@ -3181,7 +3282,7 @@
"Blocks the current thread (indefinitely!) until all actions
dispatched thus far, from this thread or agent, to the agent(s) have
occurred. Will block on failed agents. Will never return if
- a failed agent is restarted with :clear-actions true."
+ a failed agent is restarted with :clear-actions true or shutdown-agents was called."
{:added "1.0"
:static true}
[& agents]
@@ -3419,13 +3520,6 @@
:added "1.1"}
[x] (. clojure.lang.RT (charCast x)))
-(defn boolean
- "Coerce to boolean"
- {
- :inline (fn [x] `(. clojure.lang.RT (booleanCast ~x)))
- :added "1.0"}
- [x] (clojure.lang.RT/booleanCast x))
-
(defn unchecked-byte
"Coerce to byte. Subject to rounding or truncation."
{:inline (fn [x] `(. clojure.lang.RT (uncheckedByteCast ~x)))
@@ -3747,9 +3841,11 @@
(let [gx (gensym)]
`(let [~gx ~x]
~@(map (fn [f]
- (if (seq? f)
- `(~(first f) ~gx ~@(next f))
- `(~f ~gx)))
+ (with-meta
+ (if (seq? f)
+ `(~(first f) ~gx ~@(next f))
+ `(~f ~gx))
+ (meta f)))
forms)
~gx)))
@@ -4270,68 +4366,94 @@
([& keyvals]
(clojure.lang.PersistentArrayMap/createAsIfByAssoc (to-array keyvals))))
-;redefine let and loop with destructuring
+;;redefine let and loop with destructuring
(defn destructure [bindings]
(let [bents (partition 2 bindings)
pb (fn pb [bvec b v]
- (let [pvec
- (fn [bvec b val]
- (let [gvec (gensym "vec__")]
- (loop [ret (-> bvec (conj gvec) (conj val))
- n 0
- bs b
- seen-rest? false]
- (if (seq bs)
- (let [firstb (first bs)]
- (cond
- (= firstb '&) (recur (pb ret (second bs) (list `nthnext gvec n))
- n
- (nnext bs)
- true)
- (= firstb :as) (pb ret (second bs) gvec)
- :else (if seen-rest?
- (throw (new Exception "Unsupported binding form, only :as can follow & parameter"))
- (recur (pb ret firstb (list `nth gvec n nil))
- (inc n)
- (next bs)
- seen-rest?))))
- ret))))
- pmap
- (fn [bvec b v]
- (let [gmap (gensym "map__")
- gmapseq (with-meta gmap {:tag 'clojure.lang.ISeq})
- defaults (:or b)]
- (loop [ret (-> bvec (conj gmap) (conj v)
- (conj gmap) (conj `(if (seq? ~gmap) (clojure.lang.PersistentHashMap/create (seq ~gmapseq)) ~gmap))
- ((fn [ret]
- (if (:as b)
- (conj ret (:as b) gmap)
- ret))))
- bes (reduce1
- (fn [bes entry]
- (reduce1 #(assoc %1 %2 ((val entry) %2))
- (dissoc bes (key entry))
- ((key entry) bes)))
- (dissoc b :as :or)
- {:keys #(if (keyword? %) % (keyword (str %))),
- :strs str, :syms #(list `quote %)})]
- (if (seq bes)
- (let [bb (key (first bes))
- bk (val (first bes))
- bv (if (contains? defaults bb)
- (list `get gmap bk (defaults bb))
- (list `get gmap bk))]
- (recur (cond
- (symbol? bb) (-> ret (conj (if (namespace bb) (symbol (name bb)) bb)) (conj bv))
- (keyword? bb) (-> ret (conj (symbol (name bb)) bv))
- :else (pb ret bb bv))
- (next bes)))
- ret))))]
- (cond
- (symbol? b) (-> bvec (conj b) (conj v))
- (vector? b) (pvec bvec b v)
- (map? b) (pmap bvec b v)
- :else (throw (new Exception (str "Unsupported binding form: " b))))))
+ (let [pvec
+ (fn [bvec b val]
+ (let [gvec (gensym "vec__")
+ gseq (gensym "seq__")
+ gfirst (gensym "first__")
+ has-rest (some #{'&} b)]
+ (loop [ret (let [ret (conj bvec gvec val)]
+ (if has-rest
+ (conj ret gseq (list `seq gvec))
+ ret))
+ n 0
+ bs b
+ seen-rest? false]
+ (if (seq bs)
+ (let [firstb (first bs)]
+ (cond
+ (= firstb '&) (recur (pb ret (second bs) gseq)
+ n
+ (nnext bs)
+ true)
+ (= firstb :as) (pb ret (second bs) gvec)
+ :else (if seen-rest?
+ (throw (new Exception "Unsupported binding form, only :as can follow & parameter"))
+ (recur (pb (if has-rest
+ (conj ret
+ gfirst `(first ~gseq)
+ gseq `(next ~gseq))
+ ret)
+ firstb
+ (if has-rest
+ gfirst
+ (list `nth gvec n nil)))
+ (inc n)
+ (next bs)
+ seen-rest?))))
+ ret))))
+ pmap
+ (fn [bvec b v]
+ (let [gmap (gensym "map__")
+ gmapseq (with-meta gmap {:tag 'clojure.lang.ISeq})
+ defaults (:or b)]
+ (loop [ret (-> bvec (conj gmap) (conj v)
+ (conj gmap) (conj `(if (seq? ~gmap) (clojure.lang.PersistentHashMap/create (seq ~gmapseq)) ~gmap))
+ ((fn [ret]
+ (if (:as b)
+ (conj ret (:as b) gmap)
+ ret))))
+ bes (let [transforms
+ (reduce1
+ (fn [transforms mk]
+ (if (keyword? mk)
+ (let [mkns (namespace mk)
+ mkn (name mk)]
+ (cond (= mkn "keys") (assoc transforms mk #(keyword (or mkns (namespace %)) (name %)))
+ (= mkn "syms") (assoc transforms mk #(list `quote (symbol (or mkns (namespace %)) (name %))))
+ (= mkn "strs") (assoc transforms mk str)
+ :else transforms))
+ transforms))
+ {}
+ (keys b))]
+ (reduce1
+ (fn [bes entry]
+ (reduce1 #(assoc %1 %2 ((val entry) %2))
+ (dissoc bes (key entry))
+ ((key entry) bes)))
+ (dissoc b :as :or)
+ transforms))]
+ (if (seq bes)
+ (let [bb (key (first bes))
+ bk (val (first bes))
+ local (if (instance? clojure.lang.Named bb) (with-meta (symbol nil (name bb)) (meta bb)) bb)
+ bv (if (contains? defaults local)
+ (list `get gmap bk (defaults local))
+ (list `get gmap bk))]
+ (recur (if (ident? bb)
+ (-> ret (conj local bv))
+ (pb ret bb bv))
+ (next bes)))
+ ret))))]
+ (cond
+ (symbol? b) (-> bvec (conj b) (conj v))
+ (vector? b) (pvec bvec b v)
+ (map? b) (pmap bvec b v)
+ :else (throw (new Exception (str "Unsupported binding form: " b))))))
process-entry (fn [bvec b] (pb bvec (first b) (second b)))]
(if (every? symbol? (map first bents))
bindings
@@ -4820,22 +4942,44 @@
(^String [^String s start end] (. s (substring start end))))
(defn max-key
- "Returns the x for which (k x), a number, is greatest."
+ "Returns the x for which (k x), a number, is greatest.
+
+ If there are multiple such xs, the last one is returned."
{:added "1.0"
:static true}
([k x] x)
([k x y] (if (> (k x) (k y)) x y))
([k x y & more]
- (reduce1 #(max-key k %1 %2) (max-key k x y) more)))
+ (let [kx (k x) ky (k y)
+ [v kv] (if (> kx ky) [x kx] [y ky])]
+ (loop [v v kv kv more more]
+ (if more
+ (let [w (first more)
+ kw (k w)]
+ (if (>= kw kv)
+ (recur w kw (next more))
+ (recur v kv (next more))))
+ v)))))
(defn min-key
- "Returns the x for which (k x), a number, is least."
+ "Returns the x for which (k x), a number, is least.
+
+ If there are multiple such xs, the last one is returned."
{:added "1.0"
:static true}
([k x] x)
([k x y] (if (< (k x) (k y)) x y))
([k x y & more]
- (reduce1 #(min-key k %1 %2) (min-key k x y) more)))
+ (let [kx (k x) ky (k y)
+ [v kv] (if (< kx ky) [x kx] [y ky])]
+ (loop [v v kv kv more more]
+ (if more
+ (let [w (first more)
+ kw (k w)]
+ (if (<= kw kv)
+ (recur w kw (next more))
+ (recur v kv (next more))))
+ v)))))
(defn distinct
"Returns a lazy sequence of the elements of coll with duplicates removed.
@@ -5064,10 +5208,10 @@
array ret."
{:added "1.0"}
[a idx ret expr]
- `(let [a# ~a
+ `(let [a# ~a l# (alength a#)
~ret (aclone a#)]
(loop [~idx 0]
- (if (< ~idx (alength a#))
+ (if (< ~idx l#)
(do
(aset ~ret ~idx ~expr)
(recur (unchecked-inc ~idx)))
@@ -5196,6 +5340,13 @@
{:added "1.0"}
[xs] `(. clojure.lang.Numbers longs ~xs))
+(defn bytes?
+ "Return true if x is a byte array"
+ {:added "1.9"}
+ [x] (if (nil? x)
+ false
+ (-> x class .getComponentType (= Byte/TYPE))))
+
(import '(java.util.concurrent BlockingQueue LinkedBlockingQueue))
(defn seque
@@ -5663,7 +5814,7 @@
exception (Exception. message)
raw-trace (.getStackTrace exception)
boring? #(not= (.getMethodName ^StackTraceElement %) "doInvoke")
- trace (into-array (drop 2 (drop-while boring? raw-trace)))]
+ trace (into-array StackTraceElement (drop 2 (drop-while boring? raw-trace)))]
(.setStackTrace exception trace)
(throw (clojure.lang.Compiler$CompilerException.
*file*
@@ -5828,9 +5979,11 @@
'require loads a lib by loading its root resource. The root resource path
is derived from the lib name in the following manner:
Consider a lib named by the symbol 'x.y.z; it has the root directory
- /x/y/, and its root resource is /x/y/z.clj. The root
- resource should contain code to create the lib's namespace (usually by using
- the ns macro) and load any additional lib resources.
+ /x/y/, and its root resource is /x/y/z.clj, or
+ /x/y/z.cljc if /x/y/z.clj does not exist. The
+ root resource should contain code to create the lib's
+ namespace (usually by using the ns macro) and load any additional
+ lib resources.
Libspecs
@@ -5959,10 +6112,13 @@
created."
{:added "1.0"
:static true}
- ([m [k & ks] f & args]
- (if ks
- (assoc m k (apply update-in (get m k) ks f args))
- (assoc m k (apply f (get m k) args)))))
+ ([m ks f & args]
+ (let [up (fn up [m ks f args]
+ (let [[k & ks] ks]
+ (if ks
+ (assoc m k (up (get m k) ks f args))
+ (assoc m k (apply f (get m k) args)))))]
+ (up m ks f args))))
(defn update
"'Updates' a value in an associative structure, where k is a
@@ -6001,6 +6157,11 @@
:static true}
[x] (instance? clojure.lang.IPersistentList x))
+(defn seqable?
+ "Return true if the seq function is supported for x"
+ {:added "1.9"}
+ [x] (clojure.lang.RT/canSeq x))
+
(defn ifn?
"Returns true if x implements IFn. Note that many data structures
(e.g. sets and maps) implement IFn"
@@ -6045,6 +6206,11 @@
:static true}
[coll] (instance? clojure.lang.Reversible coll))
+(defn indexed?
+ "Return true if coll implements Indexed, indicating efficient lookup by index"
+ {:added "1.9"}
+ [coll] (instance? clojure.lang.Indexed coll))
+
(def ^:dynamic
^{:doc "bound in a repl thread to the most recent value printed"
:added "1.0"}
@@ -6536,9 +6702,46 @@
(load "core_deftype")
(load "core/protocols")
(load "gvec")
-(load "instant")
+
+(defmacro ^:private when-class [class-name & body]
+ `(try
+ (Class/forName ^String ~class-name)
+ ~@body
+ (catch ClassNotFoundException _#)))
+
+(when-class "java.sql.Timestamp"
+ (load "instant"))
+
+(defprotocol Inst
+ (inst-ms* [inst]))
+
+(extend-protocol Inst
+ java.util.Date
+ (inst-ms* [inst] (.getTime ^java.util.Date inst)))
+
+;; conditionally extend to Instant on Java 8+
+(when-class "java.time.Instant"
+ (load "core_instant18"))
+
+(defn inst-ms
+ "Return the number of milliseconds since January 1, 1970, 00:00:00 GMT"
+ {:added "1.9"}
+ [inst]
+ (inst-ms* inst))
+
+(defn inst?
+ "Return true if x satisfies Inst"
+ {:added "1.9"}
+ [x]
+ (satisfies? Inst x))
+
(load "uuid")
+(defn uuid?
+ "Return true if x is a java.util.UUID"
+ {:added "1.9"}
+ [x] (instance? java.util.UUID x))
+
(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
@@ -6621,6 +6824,8 @@
from-coll conjoined. A transducer may be supplied."
{:added "1.0"
:static true}
+ ([] [])
+ ([to] to)
([to from]
(if (instance? clojure.lang.IEditableCollection to)
(with-meta (persistent! (reduce conj! (transient to) from)) (meta to))
@@ -6650,7 +6855,7 @@
(defn filterv
"Returns a vector of the items in coll for which
- (pred item) returns true. pred must be free of side-effects."
+ (pred item) returns logical true. pred must be free of side-effects."
{:added "1.4"
:static true}
[pred coll]
@@ -6672,7 +6877,8 @@
(defn slurp
"Opens a reader on f and reads all its contents, returning a string.
See clojure.java.io/reader for a complete list of supported arguments."
- {:added "1.0"}
+ {:added "1.0"
+ :tag String}
([f & opts]
(let [opts (normalize-slurp-opts opts)
sw (java.io.StringWriter.)]
@@ -7110,6 +7316,18 @@
(cons x (keepi (inc idx) (rest s)))))))))]
(keepi 0 coll))))
+(defn bounded-count
+ "If coll is counted? returns its count, else will count at most the first n
+ elements of coll using its seq"
+ {:added "1.9"}
+ [n coll]
+ (if (counted? coll)
+ (count coll)
+ (loop [i 0 s (seq coll)]
+ (if (and s (< i n))
+ (recur (inc i) (next s))
+ i))))
+
(defn every-pred
"Takes a set of predicates and returns a function f that returns true if all of its
composing predicates return a logical true value against all of its arguments, else it returns
@@ -7345,6 +7563,30 @@
([result input]
(reduce rrf result input)))))
+(defn halt-when
+ "Returns a transducer that ends transduction when pred returns true
+ for an input. When retf is supplied it must be a fn of 2 arguments -
+ it will be passed the (completed) result so far and the input that
+ triggered the predicate, and its return value (if it does not throw
+ an exception) will be the return value of the transducer. If retf
+ is not supplied, the input that triggered the predicate will be
+ returned. If the predicate never returns true the transduction is
+ unaffected."
+ {:added "1.9"}
+ ([pred] (halt-when pred nil))
+ ([pred retf]
+ (fn [rf]
+ (fn
+ ([] (rf))
+ ([result]
+ (if (and (map? result) (contains? result ::halt))
+ (::halt result)
+ (rf result)))
+ ([result input]
+ (if (pred input)
+ (reduced {::halt (if retf (retf (rf result) input) input)})
+ (rf result input)))))))
+
(defn dedupe
"Returns a lazy sequence removing consecutive duplicates in coll.
Returns a transducer when no collection is provided."
@@ -7443,15 +7685,17 @@
(def ^{:added "1.4"} default-data-readers
"Default map of data reader functions provided by Clojure. May be
overridden by binding *data-readers*."
- {'inst #'clojure.instant/read-instant-date
- 'uuid #'clojure.uuid/default-uuid-reader})
+ (merge
+ {'uuid #'clojure.uuid/default-uuid-reader}
+ (when-class "java.sql.Timestamp"
+ {'inst #'clojure.instant/read-instant-date})))
(def ^{:added "1.4" :dynamic true} *data-readers*
"Map from reader tag symbols to data reader Vars.
When Clojure starts, it searches for files named 'data_readers.clj'
- at the root of the classpath. Each such file must contain a literal
- map of symbols, like this:
+ and 'data_readers.cljc' at the root of the classpath. Each such file
+ must contain a literal map of symbols, like this:
{foo/bar my.project.foo/bar
foo/baz my.project/baz}
@@ -7472,7 +7716,7 @@
Reader tags without namespace qualifiers are reserved for
Clojure. Default reader tags are defined in
clojure.core/default-data-readers but may be overridden in
- data_readers.clj or by rebinding this Var."
+ data_readers.clj, data_readers.cljc, or by rebinding this Var."
{})
(def ^{:added "1.5" :dynamic true} *default-data-reader-fn*
@@ -7532,3 +7776,8 @@
(catch Throwable t
(.printStackTrace t)
(throw t)))
+
+(defn uri?
+ "Return true if x is a java.net.URI"
+ {:added "1.9"}
+ [x] (instance? java.net.URI x))
diff --git a/src/clj/clojure/core/protocols.clj b/src/clj/clojure/core/protocols.clj
index 5c68ba35..e3bedb25 100644
--- a/src/clj/clojure/core/protocols.clj
+++ b/src/clj/clojure/core/protocols.clj
@@ -145,10 +145,11 @@
clojure.lang.StringSeq
(internal-reduce
[str-seq f val]
- (let [s (.s str-seq)]
+ (let [s (.s str-seq)
+ len (.length s)]
(loop [i (.i str-seq)
val val]
- (if (< i (.length s))
+ (if (< i len)
(let [ret (f val (.charAt s i))]
(if (reduced? ret)
@ret
diff --git a/src/clj/clojure/core_deftype.clj b/src/clj/clojure/core_deftype.clj
index 723ed24c..73a4787f 100644
--- a/src/clj/clojure/core_deftype.clj
+++ b/src/clj/clojure/core_deftype.clj
@@ -156,7 +156,9 @@
hinted-fields fields
fields (vec (map #(with-meta % nil) fields))
base-fields fields
- fields (conj fields '__meta '__extmap)
+ fields (conj fields '__meta '__extmap
+ '^:unsynchronized-mutable __hash
+ '^:unsynchronized-mutable __hasheq)
type-hash (hash classname)]
(when (some #{:volatile-mutable :unsynchronized-mutable} (mapcat (comp keys meta) hinted-fields))
(throw (IllegalArgumentException. ":volatile-mutable or :unsynchronized-mutable not supported for record fields")))
@@ -168,8 +170,18 @@
(eqhash [[i m]]
[(conj i 'clojure.lang.IHashEq)
(conj m
- `(hasheq [this#] (bit-xor ~type-hash (clojure.lang.APersistentMap/mapHasheq this#)))
- `(hashCode [this#] (clojure.lang.APersistentMap/mapHash this#))
+ `(hasheq [this#] (let [hq# ~'__hasheq]
+ (if (zero? hq#)
+ (let [h# (int (bit-xor ~type-hash (clojure.lang.APersistentMap/mapHasheq this#)))]
+ (set! ~'__hasheq h#)
+ h#)
+ hq#)))
+ `(hashCode [this#] (let [hash# ~'__hash]
+ (if (zero? hash#)
+ (let [h# (clojure.lang.APersistentMap/mapHash this#)]
+ (set! ~'__hash h#)
+ h#)
+ hash#)))
`(equals [this# ~gs] (clojure.lang.APersistentMap/mapEquals this# ~gs)))])
(iobj [[i m]]
[(conj i 'clojure.lang.IObj)
@@ -220,12 +232,12 @@
`(assoc [this# k# ~gs]
(condp identical? k#
~@(mapcat (fn [fld]
- [(keyword fld) (list* `new tagname (replace {fld gs} fields))])
+ [(keyword fld) (list* `new tagname (replace {fld gs} (remove '#{__hash __hasheq} fields)))])
base-fields)
- (new ~tagname ~@(remove #{'__extmap} fields) (assoc ~'__extmap k# ~gs))))
+ (new ~tagname ~@(remove '#{__extmap __hash __hasheq} fields) (assoc ~'__extmap k# ~gs))))
`(without [this# k#] (if (contains? #{~@(map keyword base-fields)} k#)
(dissoc (with-meta (into {} this#) ~'__meta) k#)
- (new ~tagname ~@(remove #{'__extmap} fields)
+ (new ~tagname ~@(remove '#{__extmap __hash __hasheq} fields)
(not-empty (dissoc ~'__extmap k#))))))])
(ijavamap [[i m]]
[(conj i 'java.util.Map 'java.io.Serializable)
@@ -243,8 +255,11 @@
`(entrySet [this#] (set this#)))])
]
(let [[i m] (-> [interfaces methods] irecord eqhash iobj ilookup imap ijavamap)]
- `(deftype* ~(symbol (name (ns-name *ns*)) (name tagname)) ~classname ~(conj hinted-fields '__meta '__extmap)
- :implements ~(vec i)
+ `(deftype* ~(symbol (name (ns-name *ns*)) (name tagname)) ~classname
+ ~(conj hinted-fields '__meta '__extmap
+ '^int ^:unsynchronized-mutable __hash
+ '^int ^:unsynchronized-mutable __hasheq)
+ :implements ~(vec i)
~@(mapcat identity opts)
~@m))))))
@@ -280,7 +295,7 @@
[fields name]
(when-not (vector? fields)
(throw (AssertionError. "No fields vector given.")))
- (let [specials #{'__meta '__extmap}]
+ (let [specials '#{__meta __hash __hasheq __extmap}]
(when (some specials fields)
(throw (AssertionError. (str "The names in " specials " cannot be used as field names for types or records.")))))
(let [non-syms (remove symbol? fields)]
@@ -357,9 +372,9 @@
Two constructors will be defined, one taking the designated fields
followed by a metadata map (nil for none) and an extension field
map (nil for none), and one taking only the fields (using nil for
- meta and extension fields). Note that the field names __meta
- and __extmap are currently reserved and should not be used when
- defining your own records.
+ meta and extension fields). Note that the field names __meta,
+ __extmap, __hash and __hasheq are currently reserved and should not
+ be used when defining your own records.
Given (defrecord TypeName ...), two factory functions will be
defined: ->TypeName, taking positional parameters for the fields,
@@ -410,8 +425,8 @@
Options are expressed as sequential keywords and arguments (in any order).
Supported options:
- :load-ns - if true, importing the record class will cause the
- namespace in which the record was defined to be loaded.
+ :load-ns - if true, importing the type class will cause the
+ namespace in which the type was defined to be loaded.
Defaults to false.
Each spec consists of a protocol or interface name followed by zero
@@ -465,8 +480,8 @@
writes the .class file to the *compile-path* directory.
One constructor will be defined, taking the designated fields. Note
- that the field names __meta and __extmap are currently reserved and
- should not be used when defining your own types.
+ that the field names __meta, __extmap, __hash and __hasheq are currently
+ reserved and should not be used when defining your own types.
Given (deftype TypeName ...), a factory function called ->TypeName
will be defined, taking positional parameters for the fields"
diff --git a/src/clj/clojure/core_instant18.clj b/src/clj/clojure/core_instant18.clj
new file mode 100644
index 00000000..1feb325e
--- /dev/null
+++ b/src/clj/clojure/core_instant18.clj
@@ -0,0 +1,17 @@
+; 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.
+
+(in-ns 'clojure.core)
+
+(import 'java.time.Instant)
+
+(set! *warn-on-reflection* true)
+
+(extend-protocol Inst
+ java.time.Instant
+ (inst-ms* [inst] (.toEpochMilli ^java.time.Instant inst)))
diff --git a/src/clj/clojure/core_print.clj b/src/clj/clojure/core_print.clj
index 12d8354b..1b2b7a57 100644
--- a/src/clj/clojure/core_print.clj
+++ b/src/clj/clojure/core_print.clj
@@ -38,6 +38,13 @@
(def ^:dynamic *verbose-defrecords* false)
+(def ^:dynamic
+ ^{:doc "*print-namespace-maps* controls whether the printer will print
+ namespace map literal syntax. It defaults to false, but the REPL binds
+ to true."
+ :added "1.9"}
+ *print-namespace-maps* false)
+
(defn- print-sequential [^String begin, print-one, ^String sep, ^String end, sequence, ^Writer w]
(binding [*print-level* (and (not *print-dup*) *print-level* (dec *print-level*))]
(if (and *print-level* (neg? *print-level*))
@@ -121,6 +128,20 @@
(defmethod print-method Number [o, ^Writer w]
(.write w (str o)))
+(defmethod print-method Double [o, ^Writer w]
+ (cond
+ (= Double/POSITIVE_INFINITY o) (.write w "##Inf")
+ (= Double/NEGATIVE_INFINITY o) (.write w "##-Inf")
+ (.isNaN ^Double o) (.write w "##NaN")
+ :else (.write w (str o))))
+
+(defmethod print-method Float [o, ^Writer w]
+ (cond
+ (= Float/POSITIVE_INFINITY o) (.write w "##Inf")
+ (= Float/NEGATIVE_INFINITY o) (.write w "##-Inf")
+ (.isNaN ^Float o) (.write w "##NaN")
+ :else (.write w (str o))))
+
(defmethod print-dup Number [o, ^Writer w]
(print-ctor o
(fn [o w]
@@ -205,18 +226,46 @@
(print-meta v w)
(print-sequential "[" pr-on " " "]" v w))
+(defn- print-prefix-map [prefix m print-one w]
+ (print-sequential
+ (str prefix "{")
+ (fn [e ^Writer w]
+ (do (print-one (key e) w) (.append w \space) (print-one (val e) w)))
+ ", "
+ "}"
+ (seq m) w))
+
(defn- print-map [m print-one w]
- (print-sequential
- "{"
- (fn [e ^Writer w]
- (do (print-one (key e) w) (.append w \space) (print-one (val e) w)))
- ", "
- "}"
- (seq m) w))
+ (print-prefix-map nil m print-one w))
+
+(defn- strip-ns
+ [named]
+ (if (symbol? named)
+ (symbol nil (name named))
+ (keyword nil (name named))))
+
+(defn- lift-ns
+ "Returns [lifted-ns lifted-map] or nil if m can't be lifted."
+ [m]
+ (when *print-namespace-maps*
+ (loop [ns nil
+ [[k v :as entry] & entries] (seq m)
+ lm {}]
+ (if entry
+ (when (or (keyword? k) (symbol? k))
+ (if ns
+ (when (= ns (namespace k))
+ (recur ns entries (assoc lm (strip-ns k) v)))
+ (when-let [new-ns (namespace k)]
+ (recur new-ns entries (assoc lm (strip-ns k) v)))))
+ [ns (apply conj (empty m) lm)]))))
(defmethod print-method clojure.lang.IPersistentMap [m, ^Writer w]
(print-meta m w)
- (print-map m pr-on w))
+ (let [[ns lift-map] (lift-ns m)]
+ (if ns
+ (print-prefix-map (str "#:" ns) lift-map pr-on w)
+ (print-map m pr-on w))))
(defmethod print-dup java.util.Map [m, ^Writer w]
(print-ctor m #(print-map (seq %1) print-dup %2) w))
@@ -413,18 +462,24 @@
(defmethod print-method StackTraceElement [^StackTraceElement o ^Writer w]
(print-method [(symbol (.getClassName o)) (symbol (.getMethodName o)) (.getFileName o) (.getLineNumber o)] w))
+(defn StackTraceElement->vec
+ "Constructs a data representation for a StackTraceElement"
+ {:added "1.9"}
+ [^StackTraceElement o]
+ [(symbol (.getClassName o)) (symbol (.getMethodName o)) (.getFileName o) (.getLineNumber o)])
+
(defn Throwable->map
"Constructs a data representation for a Throwable."
{:added "1.7"}
[^Throwable o]
(let [base (fn [^Throwable t]
- (let [m {:type (class t)
- :message (.getLocalizedMessage t)
- :at (get (.getStackTrace t) 0)}
- data (ex-data t)]
- (if data
- (assoc m :data data)
- m)))
+ (merge {:type (symbol (.getName (class t)))
+ :message (.getLocalizedMessage t)}
+ (when-let [ed (ex-data t)]
+ {:data ed})
+ (let [st (.getStackTrace t)]
+ (when (pos? (alength st))
+ {:at (StackTraceElement->vec (aget st 0))}))))
via (loop [via [], ^Throwable t o]
(if t
(recur (conj via t) (.getCause t))
@@ -432,7 +487,8 @@
^Throwable root (peek via)
m {:cause (.getLocalizedMessage root)
:via (vec (map base via))
- :trace (vec (.getStackTrace ^Throwable (or root o)))}
+ :trace (vec (map StackTraceElement->vec
+ (.getStackTrace ^Throwable (or root o))))}
data (ex-data root)]
(if data
(assoc m :data data)
@@ -448,9 +504,10 @@
(when-let [data (:data %)]
(.write w "\n :data ")
(print-method data w))
- (.write w "\n :at ")
- (print-method (:at %) w)
- (.write w "}"))]
+ (when-let [at (:at %)]
+ (.write w "\n :at ")
+ (print-method (:at %) w))
+ (.write w "}"))]
(print-method cause w)
(when data
(.write w "\n :data ")
diff --git a/src/clj/clojure/core_proxy.clj b/src/clj/clojure/core_proxy.clj
index 03fe2cd4..9185b4a0 100644
--- a/src/clj/clojure/core_proxy.clj
+++ b/src/clj/clojure/core_proxy.clj
@@ -13,6 +13,7 @@
(import
'(clojure.asm ClassWriter ClassVisitor Opcodes Type)
'(java.lang.reflect Modifier Constructor)
+ '(java.io Serializable NotSerializableException)
'(clojure.asm.commons Method GeneratorAdapter)
'(clojure.lang IProxy Reflector DynamicClassLoader IPersistentMap PersistentHashMap RT))
@@ -23,8 +24,10 @@
(or (some (fn [t] (when (every? #(isa? t %) rtypes) t)) rtypes)
(throw (Exception. "Incompatible return types"))))
-(defn- group-by-sig [coll]
- "takes a collection of [msig meth] and returns a seq of maps from return-types to meths."
+(defn- group-by-sig
+ "Takes a collection of [msig meth] and returns a seq of maps from
+ return-types to meths."
+ [coll]
(vals (reduce1 (fn [m [msig meth]]
(let [rtype (peek msig)
argsig (pop msig)]
@@ -44,7 +47,8 @@
(defn- generate-proxy [^Class super interfaces]
(let [cv (new ClassWriter (. ClassWriter COMPUTE_MAXS))
- cname (.replace (proxy-name super interfaces) \. \/) ;(str "clojure/lang/" (gensym "Proxy__"))
+ pname (proxy-name super interfaces)
+ cname (.replace pname \. \/) ;(str "clojure/lang/" (gensym "Proxy__"))
ctype (. Type (getObjectType cname))
iname (fn [^Class c] (.. Type (getType c) (getInternalName)))
fmap "__clojureFnMap"
@@ -148,6 +152,22 @@
(. gen (returnValue))
(. gen (endMethod)))))
+ ;disable serialization
+ (when (some #(isa? % Serializable) (cons super interfaces))
+ (let [m (. Method (getMethod "void writeObject(java.io.ObjectOutputStream)"))
+ gen (new GeneratorAdapter (. Opcodes ACC_PRIVATE) m nil nil cv)]
+ (. gen (visitCode))
+ (. gen (loadThis))
+ (. gen (loadArgs))
+ (. gen (throwException (totype NotSerializableException) pname))
+ (. gen (endMethod)))
+ (let [m (. Method (getMethod "void readObject(java.io.ObjectInputStream)"))
+ gen (new GeneratorAdapter (. Opcodes ACC_PRIVATE) m nil nil cv)]
+ (. gen (visitCode))
+ (. gen (loadThis))
+ (. gen (loadArgs))
+ (. gen (throwException (totype NotSerializableException) pname))
+ (. gen (endMethod))))
;add IProxy methods
(let [m (. Method (getMethod "void __initClojureFnMappings(clojure.lang.IPersistentMap)"))
gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)]
@@ -401,10 +421,15 @@
snapshot (fn []
(reduce1 (fn [m e]
(assoc m (key e) ((val e))))
- {} (seq pmap)))]
+ {} (seq pmap)))
+ thisfn (fn thisfn [plseq]
+ (lazy-seq
+ (when-let [pseq (seq plseq)]
+ (cons (clojure.lang.MapEntry/create (first pseq) (v (first pseq)))
+ (thisfn (rest pseq))))))]
(proxy [clojure.lang.APersistentMap]
[]
- (iterator [] (.iterator ^Iterable pmap))
+ (iterator [] (clojure.lang.SeqIterator. ^java.util.Iterator (thisfn (keys pmap))))
(containsKey [k] (contains? pmap k))
(entryAt [k] (when (contains? pmap k) (clojure.lang.MapEntry/create k (v k))))
(valAt ([k] (when (contains? pmap k) (v k)))
@@ -413,11 +438,7 @@
(count [] (count pmap))
(assoc [k v] (assoc (snapshot) k v))
(without [k] (dissoc (snapshot) k))
- (seq [] ((fn thisfn [plseq]
- (lazy-seq
- (when-let [pseq (seq plseq)]
- (cons (clojure.lang.MapEntry/create (first pseq) (v (first pseq)))
- (thisfn (rest pseq)))))) (keys pmap))))))
+ (seq [] (thisfn (keys pmap))))))
diff --git a/src/clj/clojure/gvec.clj b/src/clj/clojure/gvec.clj
index dbfe9282..3c400737 100644
--- a/src/clj/clojure/gvec.clj
+++ b/src/clj/clojure/gvec.clj
@@ -249,6 +249,7 @@
(new Vec am cnt shift (.doAssoc this shift root i val) tail (meta this)))
(= i cnt) (.cons this val)
:else (throw (IndexOutOfBoundsException.))))
+ (length [_] cnt)
clojure.lang.Reversible
(rseq [this]
@@ -474,7 +475,13 @@
:char (mk-am char)
:boolean (mk-am boolean)})
-(defn vector-of
+(defmacro ^:private ams-check [t]
+ `(let [am# (ams ~t)]
+ (if am#
+ am#
+ (throw (IllegalArgumentException. (str "Unrecognized type " ~t))))))
+
+(defn vector-of
"Creates a new vector of a single primitive type t, where t is one
of :int :long :float :double :byte :short :char or :boolean. The
resulting vector complies with the interface of vectors in general,
@@ -484,28 +491,28 @@
{:added "1.2"
:arglists '([t] [t & elements])}
([t]
- (let [am ^clojure.core.ArrayManager (ams t)]
+ (let [^clojure.core.ArrayManager am (ams-check t)]
(Vec. am 0 5 EMPTY-NODE (.array am 0) nil)))
([t x1]
- (let [am ^clojure.core.ArrayManager (ams t)
+ (let [^clojure.core.ArrayManager am (ams-check t)
arr (.array am 1)]
(.aset am arr 0 x1)
(Vec. am 1 5 EMPTY-NODE arr nil)))
([t x1 x2]
- (let [am ^clojure.core.ArrayManager (ams t)
+ (let [^clojure.core.ArrayManager am (ams-check t)
arr (.array am 2)]
(.aset am arr 0 x1)
(.aset am arr 1 x2)
(Vec. am 2 5 EMPTY-NODE arr nil)))
([t x1 x2 x3]
- (let [am ^clojure.core.ArrayManager (ams t)
+ (let [^clojure.core.ArrayManager am (ams-check t)
arr (.array am 3)]
(.aset am arr 0 x1)
(.aset am arr 1 x2)
(.aset am arr 2 x3)
(Vec. am 3 5 EMPTY-NODE arr nil)))
([t x1 x2 x3 x4]
- (let [am ^clojure.core.ArrayManager (ams t)
+ (let [^clojure.core.ArrayManager am (ams-check t)
arr (.array am 4)]
(.aset am arr 0 x1)
(.aset am arr 1 x2)
diff --git a/src/clj/clojure/instant.clj b/src/clj/clojure/instant.clj
index ddece220..9c8eb5ec 100644
--- a/src/clj/clojure/instant.clj
+++ b/src/clj/clojure/instant.clj
@@ -136,7 +136,7 @@ specified.
((if leap-year? dim-leap dim-norm) month))))
(defn validated
- "Return a function which constructs and instant by calling constructor
+ "Return a function which constructs an instant by calling constructor
after first validating that those arguments are in range and otherwise
plausible. The resulting function will throw an exception if called
with invalid arguments."
diff --git a/src/clj/clojure/java/io.clj b/src/clj/clojure/java/io.clj
index 948561a0..72a30ed7 100644
--- a/src/clj/clojure/java/io.clj
+++ b/src/clj/clojure/java/io.clj
@@ -390,7 +390,7 @@
(defn copy
"Copies input to output. Returns nil or throws IOException.
- Input may be an InputStream, Reader, File, byte[], or String.
+ Input may be an InputStream, Reader, File, byte[], char[], or String.
Output may be an OutputStream, Writer, or File.
Options are key/value pairs and may be one of
@@ -428,7 +428,7 @@
(reduce file (file parent child) more)))
(defn delete-file
- "Delete file f. Raise an exception if it fails unless silently is true."
+ "Delete file f. If silently is nil or false, raise an exception on failure, else return the value of silently."
{:added "1.2"}
[f & [silently]]
(or (.delete (file f))
diff --git a/src/clj/clojure/java/javadoc.clj b/src/clj/clojure/java/javadoc.clj
index 36527097..863de75c 100644
--- a/src/clj/clojure/java/javadoc.clj
+++ b/src/clj/clojure/java/javadoc.clj
@@ -20,20 +20,24 @@
(def ^:dynamic *core-java-api*
(case (System/getProperty "java.specification.version")
- "1.6" "http://java.sun.com/javase/6/docs/api/"
- "http://java.sun.com/javase/7/docs/api/"))
+ "1.6" "http://docs.oracle.com/javase/6/docs/api/"
+ "1.7" "http://docs.oracle.com/javase/7/docs/api/"
+ "1.8" "http://docs.oracle.com/javase/8/docs/api/"
+ "http://docs.oracle.com/javase/8/docs/api/"))
(def ^:dynamic *remote-javadocs*
(ref (sorted-map
+ "com.google.common." "http://google.github.io/guava/releases/23.0/api/docs/"
"java." *core-java-api*
"javax." *core-java-api*
"org.ietf.jgss." *core-java-api*
"org.omg." *core-java-api*
"org.w3c.dom." *core-java-api*
"org.xml.sax." *core-java-api*
- "org.apache.commons.codec." "http://commons.apache.org/codec/api-release/"
- "org.apache.commons.io." "http://commons.apache.org/io/api-release/"
- "org.apache.commons.lang." "http://commons.apache.org/lang/api-release/")))
+ "org.apache.commons.codec." "http://commons.apache.org/proper/commons-codec/apidocs/"
+ "org.apache.commons.io." "http://commons.apache.org/proper/commons-io/javadocs/api-release/"
+ "org.apache.commons.lang." "http://commons.apache.org/proper/commons-lang/javadocs/api-2.6/"
+ "org.apache.commons.lang3." "http://commons.apache.org/proper/commons-lang/javadocs/api-release/")))
(defn add-local-javadoc
"Adds to the list of local Javadoc paths."
diff --git a/src/clj/clojure/main.clj b/src/clj/clojure/main.clj
index 14af9c7e..53b1dc3f 100644
--- a/src/clj/clojure/main.clj
+++ b/src/clj/clojure/main.clj
@@ -12,6 +12,7 @@
:author "Stephen C. Gilardi and Rich Hickey"}
clojure.main
(:refer-clojure :exclude [with-bindings])
+ (:require [clojure.spec.alpha])
(:import (clojure.lang Compiler Compiler$CompilerException
LineNumberingPushbackReader RT))
;;(:use [clojure.repl :only (demunge root-cause stack-element-str)])
@@ -74,12 +75,14 @@
*print-meta* *print-meta*
*print-length* *print-length*
*print-level* *print-level*
+ *print-namespace-maps* true
*data-readers* *data-readers*
*default-data-reader-fn* *default-data-reader-fn*
*compile-path* (System/getProperty "clojure.compile.path" "classes")
*command-line-args* *command-line-args*
*unchecked-math* *unchecked-math*
*assert* *assert*
+ clojure.spec.alpha/*explain-out* clojure.spec.alpha/*explain-out*
*1 nil
*2 nil
*3 nil
diff --git a/src/clj/clojure/pprint.clj b/src/clj/clojure/pprint.clj
index f09f4da0..42836ac8 100644
--- a/src/clj/clojure/pprint.clj
+++ b/src/clj/clojure/pprint.clj
@@ -32,7 +32,7 @@ cl-format, it supports very concise custom dispatch. It also provides
a more powerful alternative to Clojure's standard format function.
See documentation for pprint and cl-format for more information or
-complete documentation on the the clojure web site on github.",
+complete documentation on the Clojure web site on GitHub.",
:added "1.2"}
clojure.pprint
(:refer-clojure :exclude (deftype))
diff --git a/src/clj/clojure/pprint/dispatch.clj b/src/clj/clojure/pprint/dispatch.clj
index 323348eb..1ef9a578 100644
--- a/src/clj/clojure/pprint/dispatch.clj
+++ b/src/clj/clojure/pprint/dispatch.clj
@@ -92,19 +92,23 @@
;;; (def pprint-map (formatter-out "~<{~;~@{~<~w~^ ~_~w~:>~^, ~_~}~;}~:>"))
(defn- pprint-map [amap]
- (pprint-logical-block :prefix "{" :suffix "}"
- (print-length-loop [aseq (seq amap)]
- (when aseq
- (pprint-logical-block
- (write-out (ffirst aseq))
- (.write ^java.io.Writer *out* " ")
- (pprint-newline :linear)
- (set! *current-length* 0) ; always print both parts of the [k v] pair
- (write-out (fnext (first aseq))))
- (when (next aseq)
- (.write ^java.io.Writer *out* ", ")
- (pprint-newline :linear)
- (recur (next aseq)))))))
+ (let [[ns lift-map] (when (not (record? amap))
+ (#'clojure.core/lift-ns amap))
+ amap (or lift-map amap)
+ prefix (if ns (str "#:" ns "{") "{")]
+ (pprint-logical-block :prefix prefix :suffix "}"
+ (print-length-loop [aseq (seq amap)]
+ (when aseq
+ (pprint-logical-block
+ (write-out (ffirst aseq))
+ (.write ^java.io.Writer *out* " ")
+ (pprint-newline :linear)
+ (set! *current-length* 0) ; always print both parts of the [k v] pair
+ (write-out (fnext (first aseq))))
+ (when (next aseq)
+ (.write ^java.io.Writer *out* ", ")
+ (pprint-newline :linear)
+ (recur (next aseq))))))))
(def ^{:private true} pprint-set (formatter-out "~<#{~;~@{~w~^ ~:_~}~;}~:>"))
diff --git a/src/clj/clojure/pprint/pretty_writer.clj b/src/clj/clojure/pprint/pretty_writer.clj
index e3a6e338..49023b82 100644
--- a/src/clj/clojure/pprint/pretty_writer.clj
+++ b/src/clj/clojure/pprint/pretty_writer.clj
@@ -40,9 +40,10 @@
[sym]
`(~sym @@~'this))
-(defmacro ^{:private true}
- setf [sym new-val]
+(defmacro ^{:private true}
+ setf
"Set the value of the field SYM to NEW-VAL"
+ [sym new-val]
`(alter @~'this assoc ~sym ~new-val))
(defmacro ^{:private true}
diff --git a/src/clj/clojure/pprint/utilities.clj b/src/clj/clojure/pprint/utilities.clj
index 53c4e973..95655bd6 100644
--- a/src/clj/clojure/pprint/utilities.clj
+++ b/src/clj/clojure/pprint/utilities.clj
@@ -50,19 +50,22 @@
[acc context]
(recur new-context (conj acc result))))))
-(defn- unzip-map [m]
- "Take a map that has pairs in the value slots and produce a pair of maps,
- the first having all the first elements of the pairs and the second all
- the second elements of the pairs"
+(defn- unzip-map
+ "Take a map that has pairs in the value slots and produce a pair of
+ maps, the first having all the first elements of the pairs and the
+ second all the second elements of the pairs"
+ [m]
[(into {} (for [[k [v1 v2]] m] [k v1]))
(into {} (for [[k [v1 v2]] m] [k v2]))])
-(defn- tuple-map [m v1]
+(defn- tuple-map
"For all the values, v, in the map, replace them with [v v1]"
+ [m v1]
(into {} (for [[k v] m] [k [v v1]])))
-(defn- rtrim [s c]
+(defn- rtrim
"Trim all instances of c from the end of sequence s"
+ [s c]
(let [len (count s)]
(if (and (pos? len) (= (nth s (dec (count s))) c))
(loop [n (dec len)]
@@ -72,8 +75,9 @@
true (recur (dec n))))
s)))
-(defn- ltrim [s c]
+(defn- ltrim
"Trim all instances of c from the beginning of sequence s"
+ [s c]
(let [len (count s)]
(if (and (pos? len) (= (nth s 0) c))
(loop [n 0]
@@ -82,24 +86,27 @@
(recur (inc n))))
s)))
-(defn- prefix-count [aseq val]
- "Return the number of times that val occurs at the start of sequence aseq,
-if val is a seq itself, count the number of times any element of val occurs at the
-beginning of aseq"
+(defn- prefix-count
+ "Return the number of times that val occurs at the start of sequence aseq,
+ if val is a seq itself, count the number of times any element of val
+ occurs at the beginning of aseq"
+ [aseq val]
(let [test (if (coll? val) (set val) #{val})]
(loop [pos 0]
(if (or (= pos (count aseq)) (not (test (nth aseq pos))))
pos
(recur (inc pos))))))
-(defn- prerr [& args]
+(defn- prerr
"Println to *err*"
+ [& args]
(binding [*out* *err*]
(apply println args)))
-
-(defmacro ^{:private true} prlabel [prefix arg & more-args]
+
+(defmacro ^{:private true} prlabel
"Print args to *err* in name = value format"
- `(prerr ~@(cons (list 'quote prefix) (mapcat #(list (list 'quote %) "=" %)
+ [prefix arg & more-args]
+ `(prerr ~@(cons (list 'quote prefix) (mapcat #(list (list 'quote %) "=" %)
(cons arg (seq more-args))))))
;; Flush the pretty-print buffer without flushing the underlying stream
diff --git a/src/clj/clojure/repl.clj b/src/clj/clojure/repl.clj
index 70ea94f5..c796df3e 100644
--- a/src/clj/clojure/repl.clj
+++ b/src/clj/clojure/repl.clj
@@ -12,6 +12,7 @@
^{:author "Chris Houser, Christophe Grand, Stephen Gilardi, Michel Salim"
:doc "Utilities meant to be used interactively at the REPL"}
clojure.repl
+ (:require [clojure.spec.alpha :as spec])
(:import (java.io LineNumberReader InputStreamReader PushbackReader)
(clojure.lang RT Reflector)))
@@ -79,27 +80,38 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}})
(defn- namespace-doc [nspace]
(assoc (meta nspace) :name (ns-name nspace)))
-(defn- print-doc [m]
+(defn- print-doc [{n :ns
+ nm :name
+ :keys [forms arglists special-form doc url macro spec]
+ :as m}]
(println "-------------------------")
- (println (str (when-let [ns (:ns m)] (str (ns-name ns) "/")) (:name m)))
+ (println (or spec (str (when n (str (ns-name n) "/")) nm)))
+ (when forms
+ (doseq [f forms]
+ (print " ")
+ (prn f)))
+ (when arglists
+ (prn arglists))
(cond
- (:forms m) (doseq [f (:forms m)]
- (print " ")
- (prn f))
- (:arglists m) (prn (:arglists m)))
- (if (:special-form m)
+ special-form
(do
(println "Special Form")
- (println " " (:doc m))
+ (println " " doc)
(if (contains? m :url)
- (when (:url m)
- (println (str "\n Please see http://clojure.org/" (:url m))))
- (println (str "\n Please see http://clojure.org/special_forms#"
- (:name m)))))
- (do
- (when (:macro m)
- (println "Macro"))
- (println " " (:doc m)))))
+ (when url
+ (println (str "\n Please see http://clojure.org/" url)))
+ (println (str "\n Please see http://clojure.org/special_forms#" nm))))
+ macro
+ (println "Macro")
+ spec
+ (println "Spec"))
+ (when doc (println " " doc))
+ (when n
+ (when-let [fnspec (spec/get-spec (symbol (str (ns-name n)) (name nm)))]
+ (println "Spec")
+ (doseq [role [:args :ret :fn]]
+ (when-let [spec (get fnspec role)]
+ (println " " (str (name role) ":") (spec/describe spec)))))))
(defn find-doc
"Prints documentation for any var whose documentation or name
@@ -118,13 +130,15 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}})
(print-doc m))))
(defmacro doc
- "Prints documentation for a var or special form given its name"
+ "Prints documentation for a var or special form given its name,
+ or for a spec if given a keyword"
{:added "1.0"}
[name]
(if-let [special-name ('{& fn catch try finally try} name)]
- (#'print-doc (#'special-doc special-name))
+ `(#'print-doc (#'special-doc '~special-name))
(cond
(special-doc-map name) `(#'print-doc (#'special-doc '~name))
+ (keyword? name) `(#'print-doc {:spec '~name :doc '~(spec/describe name)})
(find-ns name) `(#'print-doc (#'namespace-doc (find-ns '~name)))
(resolve name) `(#'print-doc (meta (var ~name))))))
@@ -181,9 +195,9 @@ str-or-pattern."
(defn dir-fn
"Returns a sorted seq of symbols naming public vars in
- a namespace"
+ a namespace or namespace alias. Looks for aliases in *ns*"
[ns]
- (sort (map first (ns-publics (the-ns ns)))))
+ (sort (map first (ns-publics (the-ns (get (ns-aliases *ns*) ns ns))))))
(defmacro dir
"Prints a sorted directory of public vars in a namespace"
diff --git a/src/clj/clojure/set.clj b/src/clj/clojure/set.clj
index 6a60d4f2..b63a0044 100644
--- a/src/clj/clojure/set.clj
+++ b/src/clj/clojure/set.clj
@@ -10,9 +10,10 @@
:author "Rich Hickey"}
clojure.set)
-(defn- bubble-max-key [k coll]
- "Move a maximal element of coll according to fn k (which returns a number)
- to the front of coll."
+(defn- bubble-max-key
+ "Move a maximal element of coll according to fn k (which returns a
+ number) to the front of coll."
+ [k coll]
(let [max (apply max-key k coll)]
(cons max (remove #(identical? max %) coll))))
diff --git a/src/clj/clojure/string.clj b/src/clj/clojure/string.clj
index 910403ee..35e0650f 100644
--- a/src/clj/clojure/string.clj
+++ b/src/clj/clojure/string.clj
@@ -317,7 +317,7 @@ Design notes for clojure.string:
(defn index-of
"Return index of value (string or char) in s, optionally searching
- forward from from-index or nil if not found."
+ forward from from-index. Return nil if value not found."
{:added "1.8"}
([^CharSequence s value]
(let [result ^long
@@ -338,7 +338,7 @@ Design notes for clojure.string:
(defn last-index-of
"Return last index of value (string or char) in s, optionally
- searching backward from from-index or nil if not found."
+ searching backward from from-index. Return nil if value not found."
{:added "1.8"}
([^CharSequence s value]
(let [result ^long
diff --git a/src/jvm/clojure/lang/APersistentMap.java b/src/jvm/clojure/lang/APersistentMap.java
index 6e01ddc2..09ad42cc 100644
--- a/src/jvm/clojure/lang/APersistentMap.java
+++ b/src/jvm/clojure/lang/APersistentMap.java
@@ -14,8 +14,8 @@
import java.util.*;
public abstract class APersistentMap extends AFn implements IPersistentMap, Map, Iterable, Serializable, MapEquivalence, IHashEq {
-int _hash = -1;
-int _hasheq = -1;
+int _hash;
+int _hasheq;
public String toString(){
return RT.printString(this);
@@ -93,11 +93,12 @@ public boolean equiv(Object obj){
return true;
}
public int hashCode(){
- if(_hash == -1)
+ int cached = this._hash;
+ if(cached == 0)
{
- this._hash = mapHash(this);
+ this._hash = cached = mapHash(this);
}
- return _hash;
+ return cached;
}
static public int mapHash(IPersistentMap m){
@@ -112,12 +113,13 @@ static public int mapHash(IPersistentMap m){
}
public int hasheq(){
- if(_hasheq == -1)
+ int cached = this._hasheq;
+ if(cached == 0)
{
//this._hasheq = mapHasheq(this);
- _hasheq = Murmur3.hashUnordered(this);
+ this._hasheq = cached = Murmur3.hashUnordered(this);
}
- return _hasheq;
+ return cached;
}
static public int mapHasheq(IPersistentMap m) {
diff --git a/src/jvm/clojure/lang/APersistentSet.java b/src/jvm/clojure/lang/APersistentSet.java
index c71eb84c..1c2ce8f4 100644
--- a/src/jvm/clojure/lang/APersistentSet.java
+++ b/src/jvm/clojure/lang/APersistentSet.java
@@ -18,8 +18,8 @@
import java.util.Set;
public abstract class APersistentSet extends AFn implements IPersistentSet, Collection, Set, Serializable, IHashEq {
-int _hash = -1;
-int _hasheq = -1;
+int _hash;
+int _hasheq;
final IPersistentMap impl;
protected APersistentSet(IPersistentMap impl){
@@ -91,10 +91,10 @@ public boolean equiv(Object obj){
}
public int hashCode(){
- if(_hash == -1)
+ int hash = this._hash;
+ if(hash == 0)
{
//int hash = count();
- int hash = 0;
for(ISeq s = seq(); s != null; s = s.next())
{
Object e = s.first();
@@ -103,11 +103,12 @@ public int hashCode(){
}
this._hash = hash;
}
- return _hash;
+ return hash;
}
public int hasheq(){
- if(_hasheq == -1){
+ int cached = this._hasheq;
+ if(cached == 0){
// int hash = 0;
// for(ISeq s = seq(); s != null; s = s.next())
// {
@@ -115,9 +116,9 @@ public int hasheq(){
// hash += Util.hasheq(e);
// }
// this._hasheq = hash;
- _hasheq = Murmur3.hashUnordered(this);
+ this._hasheq = cached = Murmur3.hashUnordered(this);
}
- return _hasheq;
+ return cached;
}
public Object[] toArray(){
diff --git a/src/jvm/clojure/lang/APersistentVector.java b/src/jvm/clojure/lang/APersistentVector.java
index b55ead84..f87ad7a0 100644
--- a/src/jvm/clojure/lang/APersistentVector.java
+++ b/src/jvm/clojure/lang/APersistentVector.java
@@ -19,8 +19,8 @@ public abstract class APersistentVector extends AFn implements IPersistentVector
List,
RandomAccess, Comparable,
Serializable, IHashEq {
-int _hash = -1;
-int _hasheq = -1;
+int _hash;
+int _hasheq;
public String toString(){
return RT.printString(this);
@@ -139,9 +139,10 @@ public boolean equiv(Object obj){
}
public int hashCode(){
- if(_hash == -1)
+ int hash = this._hash;
+ if(hash == 0)
{
- int hash = 1;
+ hash = 1;
for(int i = 0;i 0)
@@ -637,6 +643,8 @@ public static class VarExpr implements Expr, AssignableExpr{
final static Method getMethod = Method.getMethod("Object get()");
final static Method setMethod = Method.getMethod("Object set(Object)");
+ Class jc;
+
public VarExpr(Var var, Symbol tag){
this.var = var;
this.tag = tag != null ? tag : var.getTag();
@@ -659,7 +667,9 @@ public boolean hasJavaClass(){
}
public Class getJavaClass() {
- return HostExpr.tagToClass(tag);
+ if (jc == null)
+ jc = HostExpr.tagToClass(tag);
+ return jc;
}
public Object evalAssign(Expr val) {
@@ -1005,12 +1015,13 @@ else if(instance != null && instance.hasJavaClass() && instance.getJavaClass() !
Symbol sym = (Symbol) RT.first(call);
Symbol tag = tagOf(form);
PersistentVector args = PersistentVector.EMPTY;
+ boolean tailPosition = inTailCall(context);
for(ISeq s = RT.next(call); s != null; s = s.next())
args = args.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, s.first()));
if(c != null)
- return new StaticMethodExpr(source, line, column, tag, c, munge(sym.name), args);
+ return new StaticMethodExpr(source, line, column, tag, c, munge(sym.name), args, tailPosition);
else
- return new InstanceMethodExpr(source, line, column, tag, instance, munge(sym.name), args);
+ return new InstanceMethodExpr(source, line, column, tag, instance, munge(sym.name), args, tailPosition);
}
}
}
@@ -1027,7 +1038,7 @@ 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.classForName(sym.name);
+ c = RT.classForNameNonLoading(sym.name);
else
{
Object o = currentNS().getMapping(sym);
@@ -1038,7 +1049,7 @@ else if(LOCAL_ENV.deref() != null && ((java.util.Map)LOCAL_ENV.deref()).contains
else
{
try{
- c = RT.classForName(sym.name);
+ c = RT.classForNameNonLoading(sym.name);
}
catch(Exception e){
// aargh
@@ -1049,7 +1060,7 @@ else if(LOCAL_ENV.deref() != null && ((java.util.Map)LOCAL_ENV.deref()).contains
}
}
else if(stringOk && form instanceof String)
- c = RT.classForName((String) form);
+ c = RT.classForNameNonLoading((String) form);
return c;
}
@@ -1134,6 +1145,7 @@ static class InstanceFieldExpr extends FieldExpr implements AssignableExpr{
final static Method invokeNoArgInstanceMember = Method.getMethod("Object invokeNoArgInstanceMember(Object,String,boolean)");
final static Method setInstanceFieldMethod = Method.getMethod("Object setInstanceField(Object,String,Object)");
+ Class jc;
public InstanceFieldExpr(int line, int column, Expr target, String fieldName, Symbol tag, boolean requireField) {
this.target = target;
@@ -1213,7 +1225,9 @@ public boolean hasJavaClass() {
}
public Class getJavaClass() {
- return tag != null ? HostExpr.tagToClass(tag) : field.getType();
+ if (jc == null)
+ jc = tag != null ? HostExpr.tagToClass(tag) : field.getType();
+ return jc;
}
public Object evalAssign(Expr val) {
@@ -1256,6 +1270,8 @@ static class StaticFieldExpr extends FieldExpr implements AssignableExpr{
final int line;
final int column;
+ Class jc;
+
public StaticFieldExpr(int line, int column, Class c, String fieldName, Symbol tag) {
//this.className = className;
this.fieldName = fieldName;
@@ -1309,7 +1325,9 @@ public boolean hasJavaClass(){
public Class getJavaClass() {
//Class c = Class.forName(className);
//java.lang.reflect.Field field = c.getField(fieldName);
- return tag != null ? HostExpr.tagToClass(tag) : field.getType();
+ if (jc == null)
+ jc =tag != null ? HostExpr.tagToClass(tag) : field.getType();
+ return jc;
}
public Object evalAssign(Expr val) {
@@ -1440,13 +1458,16 @@ static class InstanceMethodExpr extends MethodExpr{
public final int line;
public final int column;
public final Symbol tag;
+ public final boolean tailPosition;
public final java.lang.reflect.Method method;
+ Class jc;
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, IPersistentVector args)
+ public InstanceMethodExpr(String source, int line, int column, Symbol tag, Expr target,
+ String methodName, IPersistentVector args, boolean tailPosition)
{
this.source = source;
this.line = line;
@@ -1455,6 +1476,7 @@ public InstanceMethodExpr(String source, int line, int column, Symbol tag, Expr
this.methodName = methodName;
this.target = target;
this.tag = tag;
+ this.tailPosition = tailPosition;
if(target.hasJavaClass() && target.getJavaClass() != null)
{
List methods = Reflector.getMethods(target.getJavaClass(), args.count(), methodName, false);
@@ -1548,10 +1570,10 @@ public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){
gen.checkCast(type);
MethodExpr.emitTypedArgs(objx, gen, method.getParameterTypes(), args);
gen.visitLineNumber(line, gen.mark());
- if(context == C.RETURN)
+ if(tailPosition && !objx.canBeDirect)
{
ObjMethod method = (ObjMethod) METHOD.deref();
- method.emitClearLocals(gen);
+ method.emitClearThis(gen);
}
Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method));
if(method.getDeclaringClass().isInterface())
@@ -1607,7 +1629,9 @@ public boolean hasJavaClass(){
}
public Class getJavaClass() {
- return retType((tag!=null)?HostExpr.tagToClass(tag):null, (method!=null)?method.getReturnType():null);
+ if (jc == null)
+ jc = retType((tag!=null)?HostExpr.tagToClass(tag):null, (method!=null)?method.getReturnType():null);
+ return jc;
}
}
@@ -1622,12 +1646,15 @@ static class StaticMethodExpr extends MethodExpr{
public final int column;
public final java.lang.reflect.Method method;
public final Symbol tag;
+ public final boolean tailPosition;
final static Method forNameMethod = Method.getMethod("Class classForName(String)");
final static Method invokeStaticMethodMethod =
Method.getMethod("Object invokeStaticMethod(Class,String,Object[])");
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, IPersistentVector args)
+ public StaticMethodExpr(String source, int line, int column, Symbol tag, Class c,
+ String methodName, IPersistentVector args, boolean tailPosition)
{
this.c = c;
this.methodName = methodName;
@@ -1636,6 +1663,7 @@ public StaticMethodExpr(String source, int line, int column, Symbol tag, Class c
this.line = line;
this.column = column;
this.tag = tag;
+ this.tailPosition = tailPosition;
List methods = Reflector.getMethods(c, args.count(), methodName, true);
if(methods.isEmpty())
@@ -1774,10 +1802,10 @@ public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
MethodExpr.emitTypedArgs(objx, gen, method.getParameterTypes(), args);
gen.visitLineNumber(line, gen.mark());
//Type type = Type.getObjectType(className.replace('.', '/'));
- if(context == C.RETURN)
+ if(tailPosition && !objx.canBeDirect)
{
ObjMethod method = (ObjMethod) METHOD.deref();
- method.emitClearLocals(gen);
+ method.emitClearThis(gen);
}
Type type = Type.getType(c);
Method m = new Method(methodName, Type.getReturnType(method), Type.getArgumentTypes(method));
@@ -1820,7 +1848,9 @@ public boolean hasJavaClass(){
}
public Class getJavaClass() {
- return retType((tag!=null)?HostExpr.tagToClass(tag):null, (method!=null)?method.getReturnType():null);
+ if (jc == null)
+ jc = retType((tag!=null)?HostExpr.tagToClass(tag):null, (method!=null)?method.getReturnType():null);
+ return jc;
}
}
@@ -2271,13 +2301,14 @@ public Expr parse(C context, Object frm) {
}
else
{
- if(bodyExpr == null)
- try {
- Var.pushThreadBindings(RT.map(NO_RECUR, true));
- bodyExpr = (new BodyExpr.Parser()).parse(context, RT.seq(body));
- } finally {
- Var.popThreadBindings();
- }
+ if(bodyExpr == null)
+ try {
+ Var.pushThreadBindings(RT.map(NO_RECUR, true, METHOD_RETURN_CONTEXT, null));
+ bodyExpr = (new BodyExpr.Parser()).parse(context, RT.seq(body));
+ } finally {
+ Var.popThreadBindings();
+ }
+
if(Util.equals(op, CATCH))
{
Class c = HostExpr.maybeClass(RT.second(f), false);
@@ -2325,17 +2356,21 @@ public Expr parse(C context, Object frm) {
}
}
}
- if(bodyExpr == null) {
- try
- {
- Var.pushThreadBindings(RT.map(NO_RECUR, true));
- bodyExpr = (new BodyExpr.Parser()).parse(C.EXPRESSION, RT.seq(body));
- }
- finally
- {
- Var.popThreadBindings();
- }
- }
+ if(bodyExpr == null)
+ {
+ // this codepath is hit when there is neither catch or finally, e.g. (try (expr))
+ // return a body expr directly
+ try
+ {
+ Var.pushThreadBindings(RT.map(NO_RECUR, true));
+ bodyExpr = (new BodyExpr.Parser()).parse(context, RT.seq(body));
+ }
+ finally
+ {
+ Var.popThreadBindings();
+ }
+ return bodyExpr;
+ }
return new TryExpr(bodyExpr, catches, finallyExpr, retLocal,
finallyLocal);
@@ -2587,11 +2622,6 @@ public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
gen.newInstance(type);
gen.dup();
MethodExpr.emitTypedArgs(objx, gen, ctor.getParameterTypes(), args);
- if(context == C.RETURN)
- {
- ObjMethod method = (ObjMethod) METHOD.deref();
- method.emitClearLocals(gen);
- }
gen.invokeConstructor(type, new Method("", Type.getConstructorDescriptor(ctor)));
}
else
@@ -2599,11 +2629,6 @@ public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
gen.push(destubClassName(c.getName()));
gen.invokeStatic(RT_TYPE, forNameMethod);
MethodExpr.emitArgsAsArray(args, objx, gen);
- if(context == C.RETURN)
- {
- ObjMethod method = (ObjMethod) METHOD.deref();
- method.emitClearLocals(gen);
- }
gen.invokeStatic(REFLECTOR_TYPE, invokeConstructorMethod);
}
if(context == C.STATEMENT)
@@ -3260,6 +3285,7 @@ static class KeywordInvokeExpr implements Expr{
public final int siteIndex;
public final String source;
static Type ILOOKUP_TYPE = Type.getType(ILookup.class);
+ Class jc;
public KeywordInvokeExpr(String source, int line, int column, Symbol tag, KeywordExpr kw, Expr target){
this.source = source;
@@ -3324,7 +3350,9 @@ public boolean hasJavaClass() {
}
public Class getJavaClass() {
- return HostExpr.tagToClass(tag);
+ if(jc == null)
+ jc = HostExpr.tagToClass(tag);
+ return jc;
}
}
@@ -3431,16 +3459,19 @@ static class StaticInvokeExpr implements Expr, MaybePrimitiveExpr{
public final Type[] paramtypes;
public final IPersistentVector args;
public final boolean variadic;
+ public final boolean tailPosition;
public final Object tag;
+ Class jc;
StaticInvokeExpr(Type target, Class retClass, Class[] paramclasses, Type[] paramtypes, boolean variadic,
- IPersistentVector args,Object tag){
+ IPersistentVector args,Object tag, boolean tailPosition){
this.target = target;
this.retClass = retClass;
this.paramclasses = paramclasses;
this.paramtypes = paramtypes;
this.args = args;
this.variadic = variadic;
+ this.tailPosition = tailPosition;
this.tag = tag;
}
@@ -3466,7 +3497,9 @@ public boolean hasJavaClass() {
}
public Class getJavaClass() {
- return retType((tag!=null)?HostExpr.tagToClass(tag):null, retClass);
+ if(jc == null)
+ jc =retType((tag!=null)?HostExpr.tagToClass(tag):null, retClass);
+ return jc;
}
public boolean canEmitPrimitive(){
@@ -3497,6 +3530,12 @@ public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen){
else
MethodExpr.emitTypedArgs(objx, gen, paramclasses, args);
+ if(tailPosition && !objx.canBeDirect)
+ {
+ ObjMethod method = (ObjMethod) METHOD.deref();
+ method.emitClearThis(gen);
+ }
+
gen.invokeStatic(target, ms);
}
@@ -3504,7 +3543,7 @@ private Type getReturnType(){
return Type.getType(retClass);
}
- public static Expr parse(Var v, ISeq args, Object tag) {
+ public static Expr parse(Var v, ISeq args, Object tag, boolean tailPosition) {
if(!v.isBound() || v.get() == null)
{
// System.out.println("Not bound: " + v);
@@ -3560,7 +3599,7 @@ else if(argcount > params.length
for(ISeq s = RT.seq(args); s != null; s = s.next())
argv = argv.cons(analyze(C.EXPRESSION, s.first()));
- return new StaticInvokeExpr(target,retClass,paramClasses, paramTypes,variadic, argv, tag);
+ return new StaticInvokeExpr(target,retClass,paramClasses, paramTypes,variadic, argv, tag, tailPosition);
}
}
@@ -3571,6 +3610,7 @@ static class InvokeExpr implements Expr{
public final IPersistentVector args;
public final int line;
public final int column;
+ public final boolean tailPosition;
public final String source;
public boolean isProtocol = false;
public boolean isDirect = false;
@@ -3579,6 +3619,7 @@ static class InvokeExpr implements Expr{
public java.lang.reflect.Method onMethod;
static Keyword onKey = Keyword.intern("on");
static Keyword methodMapKey = Keyword.intern("method-map");
+ Class jc;
static Object sigTag(int argcount, Var v){
Object arglists = RT.get(RT.meta(v), arglistsKey);
@@ -3593,12 +3634,14 @@ static Object sigTag(int argcount, Var v){
return null;
}
- public InvokeExpr(String source, int line, int column, Symbol tag, Expr fexpr, IPersistentVector args) {
+ public InvokeExpr(String source, int line, int column, Symbol tag, Expr fexpr, IPersistentVector args, boolean tailPosition) {
this.source = source;
this.fexpr = fexpr;
this.args = args;
this.line = line;
this.column = column;
+ this.tailPosition = tailPosition;
+
if(fexpr instanceof VarExpr)
{
Var fvar = ((VarExpr)fexpr).var;
@@ -3712,6 +3755,7 @@ public void emitProto(C context, ObjExpr objx, GeneratorAdapter gen){
gen.mark(onLabel); //target
if(protocolOn != null)
{
+ gen.checkCast(Type.getType(protocolOn));
MethodExpr.emitTypedArgs(objx, gen, onMethod.getParameterTypes(), RT.subvec(args,1,args.count()));
if(context == C.RETURN)
{
@@ -3742,10 +3786,10 @@ void emitArgsAndCall(int firstArgToEmit, C context, ObjExpr objx, GeneratorAdapt
}
gen.visitLineNumber(line, gen.mark());
- if(context == C.RETURN)
+ if(tailPosition && !objx.canBeDirect)
{
ObjMethod method = (ObjMethod) METHOD.deref();
- method.emitClearLocals(gen);
+ method.emitClearThis(gen);
}
gen.invokeInterface(IFN_TYPE, new Method("invoke", OBJECT_TYPE, ARG_TYPES[Math.min(MAX_POSITIONAL_ARITY + 1,
@@ -3757,10 +3801,13 @@ public boolean hasJavaClass() {
}
public Class getJavaClass() {
- return HostExpr.tagToClass(tag);
+ if (jc == null)
+ jc = HostExpr.tagToClass(tag);
+ return jc;
}
static public Expr parse(C context, ISeq form) {
+ boolean tailPosition = inTailCall(context);
if(context != C.EVAL)
context = C.EXPRESSION;
Expr fexpr = analyze(context, form.first());
@@ -3790,7 +3837,7 @@ static public Expr parse(C context, ISeq form) {
Object sigtag = sigTag(arity, v);
Object vtag = RT.get(RT.meta(v), RT.TAG_KEY);
Expr ret = StaticInvokeExpr
- .parse(v, RT.next(form), formtag != null ? formtag : sigtag != null ? sigtag : vtag);
+ .parse(v, RT.next(form), formtag != null ? formtag : sigtag != null ? sigtag : vtag, tailPosition);
if(ret != null)
{
// System.out.println("invoke direct: " + v);
@@ -3837,7 +3884,7 @@ static public Expr parse(C context, ISeq form) {
// 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);
+ return new InvokeExpr((String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form), fexpr, args, tailPosition);
}
}
@@ -3862,6 +3909,7 @@ static public class FnExpr extends ObjExpr{
private boolean hasMeta;
private boolean hasEnclosingMethod;
// String superName = null;
+ Class jc;
public FnExpr(Object tag){
super(tag);
@@ -3876,7 +3924,9 @@ boolean supportsMeta(){
}
public Class getJavaClass() {
- return tag != null ? HostExpr.tagToClass(tag) : AFunction.class;
+ if (jc == null)
+ jc = tag != null ? HostExpr.tagToClass(tag) : AFunction.class;
+ return jc;
}
protected void emitMethods(ClassVisitor cv){
@@ -4414,8 +4464,34 @@ void compile(String superName, String[] interfaceNames, boolean oneTimeUse) thro
ctorgen.visitCode();
ctorgen.loadThis();
ctorgen.loadArgs();
- for(int i=0;i", Type.VOID_TYPE, ctorTypes));
+
+ ctorgen.returnValue();
+ ctorgen.endMethod();
+
+ // alt ctor no __hash, __hasheq
+ altCtorTypes = new Type[ctorTypes.length-2];
+ for(int i=0;i", Type.VOID_TYPE, altCtorTypes);
+ ctorgen = new GeneratorAdapter(ACC_PUBLIC,
+ alt,
+ null,
+ null,
+ cv);
+ ctorgen.visitCode();
+ ctorgen.loadThis();
+ ctorgen.loadArgs();
+
+ ctorgen.visitInsn(Opcodes.ICONST_0); //__hash
+ ctorgen.visitInsn(Opcodes.ICONST_0); //__hasheq
ctorgen.invokeConstructor(objtype, new Method("", Type.VOID_TYPE, ctorTypes));
@@ -4978,10 +5054,13 @@ public boolean hasJavaClass() {
return true;
}
+ Class jc;
public Class getJavaClass() {
- return (compiledClass != null) ? compiledClass
- : (tag != null) ? HostExpr.tagToClass(tag)
- : IFn.class;
+ if (jc == null)
+ jc = (compiledClass != null) ? compiledClass
+ : (tag != null) ? HostExpr.tagToClass(tag)
+ : IFn.class;
+ return jc;
}
public void emitAssignLocal(GeneratorAdapter gen, LocalBinding lb,Expr val){
@@ -5269,6 +5348,7 @@ static FnMethod parse(ObjExpr objx, ISeq form, Object rettag) {
,CLEAR_PATH, pnode
,CLEAR_ROOT, pnode
,CLEAR_SITES, PersistentHashMap.EMPTY
+ ,METHOD_RETURN_CONTEXT, RT.T
));
method.prim = primInterface(parms);
@@ -5846,6 +5926,11 @@ void emitClearLocalsOld(GeneratorAdapter gen){
}
}
}
+
+ void emitClearThis(GeneratorAdapter gen) {
+ gen.visitInsn(Opcodes.ACONST_NULL);
+ gen.visitVarInsn(Opcodes.ASTORE, 0);
+ }
}
public static class LocalBinding{
@@ -5858,6 +5943,7 @@ public static class LocalBinding{
public final PathNode clearPathRoot;
public boolean canBeCleared = !RT.booleanCast(getCompilerOption(disableLocalsClearingKey));
public boolean recurMistmatch = false;
+ public boolean used = false;
public LocalBinding(int num, Symbol sym, Symbol tag, Expr init, boolean isArg,PathNode clearPathRoot)
{
@@ -5872,18 +5958,25 @@ public LocalBinding(int num, Symbol sym, Symbol tag, Expr init, boolean isArg,Pa
name = munge(sym.name);
}
+ Boolean hjc;
+
public boolean hasJavaClass() {
- if(init != null && init.hasJavaClass()
- && Util.isPrimitive(init.getJavaClass())
- && !(init instanceof MaybePrimitiveExpr))
- return false;
- return tag != null
- || (init != null && init.hasJavaClass());
- }
+ if (hjc == null)
+ {
+ if(init != null && init.hasJavaClass() && Util.isPrimitive(init.getJavaClass()) && !(init instanceof MaybePrimitiveExpr))
+ hjc = false;
+ else
+ hjc = tag != null || (init != null && init.hasJavaClass());
+ }
+ return hjc;
+ }
+
+ Class jc;
public Class getJavaClass() {
- return tag != null ? HostExpr.tagToClass(tag)
- : init.getJavaClass();
+ if (jc == null)
+ jc = tag != null ? HostExpr.tagToClass(tag) : init.getJavaClass();
+ return jc;
}
public Class getPrimitiveType(){
@@ -5910,6 +6003,7 @@ public LocalBindingExpr(LocalBinding b, Symbol tag)
this.clearPath = (PathNode)CLEAR_PATH.get();
this.clearRoot = (PathNode)CLEAR_ROOT.get();
IPersistentCollection sites = (IPersistentCollection) RT.get(CLEAR_SITES.get(),b);
+ b.used = true;
if(b.idx > 0)
{
@@ -5970,10 +6064,15 @@ public boolean hasJavaClass() {
return tag != null || b.hasJavaClass();
}
+ Class jc;
public Class getJavaClass() {
- if(tag != null)
- return HostExpr.tagToClass(tag);
- return b.getJavaClass();
+ if (jc == null) {
+ if(tag != null)
+ jc = HostExpr.tagToClass(tag);
+ else
+ jc = b.getJavaClass();
+ }
+ return jc;
}
@@ -6271,14 +6370,14 @@ public Expr parse(C context, Object frm) {
{
if(recurMismatches != null && RT.booleanCast(recurMismatches.nth(i/2)))
{
- init = new StaticMethodExpr("", 0, 0, null, RT.class, "box", RT.vector(init));
+ init = new StaticMethodExpr("", 0, 0, null, RT.class, "box", RT.vector(init), false);
if(RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
RT.errPrintWriter().println("Auto-boxing loop arg: " + sym);
}
else if(maybePrimitiveType(init) == int.class)
- init = new StaticMethodExpr("", 0, 0, null, RT.class, "longCast", RT.vector(init));
+ init = new StaticMethodExpr("", 0, 0, null, RT.class, "longCast", RT.vector(init), false);
else if(maybePrimitiveType(init) == float.class)
- init = new StaticMethodExpr("", 0, 0, null, RT.class, "doubleCast", RT.vector(init));
+ init = new StaticMethodExpr("", 0, 0, null, RT.class, "doubleCast", RT.vector(init), false);
}
//sequential enhancement of env (like Lisp let*)
try
@@ -6310,10 +6409,12 @@ else if(maybePrimitiveType(init) == float.class)
try {
if(isLoop)
{
+ Object methodReturnContext = context == C.RETURN ? METHOD_RETURN_CONTEXT.deref() : null;
Var.pushThreadBindings(
RT.map(CLEAR_PATH, clearpath,
CLEAR_ROOT, clearroot,
- NO_RECUR, null));
+ NO_RECUR, null,
+ METHOD_RETURN_CONTEXT, methodReturnContext));
}
bodyExpr = (new BodyExpr.Parser()).parse(isLoop ? C.RETURN : context, body);
@@ -6371,7 +6472,10 @@ public void doEmit(C context, ObjExpr objx, GeneratorAdapter gen, boolean emitUn
else
{
bi.init.emit(C.EXPRESSION, objx, gen);
- gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx);
+ if (!bi.binding.used && bi.binding.canBeCleared)
+ gen.pop();
+ else
+ gen.visitVarInsn(OBJECT_TYPE.getOpcode(Opcodes.ISTORE), bi.binding.idx);
}
bindingLabels.put(bi, gen.mark());
}
@@ -6760,6 +6864,35 @@ public static Object preserveTag(ISeq src, Object dst) {
return dst;
}
+private static volatile Var MACRO_CHECK = null;
+private static volatile boolean MACRO_CHECK_LOADING = false;
+private static final Object MACRO_CHECK_LOCK = new Object();
+
+private static Var ensureMacroCheck() throws ClassNotFoundException, IOException {
+ if(MACRO_CHECK == null) {
+ synchronized(MACRO_CHECK_LOCK) {
+ if(MACRO_CHECK == null) {
+ MACRO_CHECK_LOADING = true;
+ RT.load("clojure/spec/alpha");
+ RT.load("clojure/core/specs/alpha");
+ MACRO_CHECK = Var.find(Symbol.intern("clojure.spec.alpha", "macroexpand-check"));
+ MACRO_CHECK_LOADING = false;
+ }
+ }
+ }
+ return MACRO_CHECK;
+}
+
+public static void checkSpecs(Var v, ISeq form) {
+ if(RT.CHECK_SPECS && !MACRO_CHECK_LOADING) {
+ try {
+ ensureMacroCheck().applyTo(RT.cons(v, RT.list(form.next())));
+ } catch(Exception e) {
+ throw new CompilerException((String) SOURCE_PATH.deref(), lineDeref(), columnDeref(), e);
+ }
+ }
+}
+
public static Object macroexpand1(Object x) {
if(x instanceof ISeq)
{
@@ -6771,17 +6904,19 @@ public static Object macroexpand1(Object x) {
Var v = isMacro(op);
if(v != null)
{
+ checkSpecs(v, form);
+
try
{
- return v.applyTo(RT.cons(form,RT.cons(LOCAL_ENV.get(),form.next())));
+ ISeq args = RT.cons(form, RT.cons(Compiler.LOCAL_ENV.get(), form.next()));
+ return v.applyTo(args);
}
catch(ArityException e)
{
// hide the 2 extra params for a macro
throw new ArityException(e.actual - 2, e.name);
}
- }
- else
+ } else
{
if(op instanceof Symbol)
{
@@ -7742,7 +7877,11 @@ static ObjExpr build(IPersistentVector interfaceSyms, IPersistentVector fieldSym
//use array map to preserve ctor order
ret.closes = new PersistentArrayMap(closesvec);
ret.fields = fmap;
- for(int i=fieldSyms.count()-1;i >= 0 && (((Symbol)fieldSyms.nth(i)).name.equals("__meta") || ((Symbol)fieldSyms.nth(i)).name.equals("__extmap"));--i)
+ for(int i=fieldSyms.count()-1;i >= 0 && (((Symbol)fieldSyms.nth(i)).name.equals("__meta")
+ || ((Symbol)fieldSyms.nth(i)).name.equals("__extmap")
+ || ((Symbol)fieldSyms.nth(i)).name.equals("__hash")
+ || ((Symbol)fieldSyms.nth(i)).name.equals("__hasheq")
+ );--i)
ret.altCtorDrops++;
}
//todo - set up volatiles
@@ -7886,8 +8025,35 @@ static Class compileStub(String superName, NewInstanceExpr ret, String[] interfa
ctorgen.visitCode();
ctorgen.loadThis();
ctorgen.loadArgs();
- for(int i=0;i", Type.VOID_TYPE, ctorTypes));
+
+ ctorgen.returnValue();
+ ctorgen.endMethod();
+
+ // alt ctor no __hash, __hasheq
+ altCtorTypes = new Type[ctorTypes.length-2];
+ for(int i=0;i", Type.VOID_TYPE, altCtorTypes);
+ ctorgen = new GeneratorAdapter(ACC_PUBLIC,
+ alt,
+ null,
+ null,
+ cv);
+ ctorgen.visitCode();
+ ctorgen.loadThis();
+ ctorgen.loadArgs();
+
+ ctorgen.visitInsn(Opcodes.ICONST_0); //__hash
+ ctorgen.visitInsn(Opcodes.ICONST_0); //__hasheq
ctorgen.invokeConstructor(Type.getObjectType(COMPILE_STUB_PREFIX + "/" + ret.internalName),
new Method("", Type.VOID_TYPE, ctorTypes));
@@ -7982,9 +8148,11 @@ protected void emitStatics(ClassVisitor cv) {
}
}
- mv.visitInsn(ACONST_NULL);
- mv.visitVarInsn(ALOAD, 0);
+ mv.visitInsn(ACONST_NULL); //__meta
+ mv.visitVarInsn(ALOAD, 0); //__extmap
mv.visitMethodInsn(INVOKESTATIC, "clojure/lang/RT", "seqOrElse", "(Ljava/lang/Object;)Ljava/lang/Object;");
+ mv.visitInsn(ICONST_0); //__hash
+ mv.visitInsn(ICONST_0); //__hasheq
mv.visitMethodInsn(INVOKESPECIAL, className, "", ctor.getDescriptor());
mv.visitInsn(ARETURN);
mv.visitMaxs(4+fieldCount, 1+fieldCount);
@@ -8164,6 +8332,7 @@ static NewInstanceMethod parse(ObjExpr objx, ISeq form, Symbol thistag,
,CLEAR_PATH, pnode
,CLEAR_ROOT, pnode
,CLEAR_SITES, PersistentHashMap.EMPTY
+ ,METHOD_RETURN_CONTEXT, RT.T
));
//register 'this' as local 0
diff --git a/src/jvm/clojure/lang/Delay.java b/src/jvm/clojure/lang/Delay.java
index ef8110b9..262c9c1a 100644
--- a/src/jvm/clojure/lang/Delay.java
+++ b/src/jvm/clojure/lang/Delay.java
@@ -13,9 +13,9 @@
package clojure.lang;
public class Delay implements IDeref, IPending{
-Object val;
-Throwable exception;
-IFn fn;
+volatile Object val;
+volatile Throwable exception;
+volatile IFn fn;
public Delay(IFn fn){
this.fn = fn;
@@ -29,18 +29,25 @@ static public Object force(Object x) {
: x;
}
-synchronized public Object deref() {
+public Object deref() {
if(fn != null)
{
- try
- {
- val = fn.invoke();
- }
- catch(Throwable t)
- {
- exception = t;
- }
- fn = null;
+ synchronized(this)
+ {
+ //double check
+ if(fn!=null)
+ {
+ try
+ {
+ val = fn.invoke();
+ }
+ catch(Throwable t)
+ {
+ exception = t;
+ }
+ fn = null;
+ }
+ }
}
if(exception != null)
throw Util.sneakyThrow(exception);
diff --git a/src/jvm/clojure/lang/EdnReader.java b/src/jvm/clojure/lang/EdnReader.java
index 08cfe20f..e7449697 100644
--- a/src/jvm/clojure/lang/EdnReader.java
+++ b/src/jvm/clojure/lang/EdnReader.java
@@ -16,6 +16,7 @@
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -48,11 +49,13 @@ public class EdnReader{
macros['#'] = new DispatchReader();
+ dispatchMacros['#'] = new SymbolicValueReader();
dispatchMacros['^'] = new MetaReader();
//dispatchMacros['"'] = new RegexReader();
dispatchMacros['{'] = new SetReader();
dispatchMacros['<'] = new UnreadableReader();
dispatchMacros['_'] = new DiscardReader();
+ dispatchMacros[':'] = new NamespaceMapReader();
}
static boolean nonConstituent(int ch){
@@ -482,6 +485,55 @@ public Object invoke(Object reader, Object underscore, Object opts) {
}
}
+public static class NamespaceMapReader extends AFn{
+ public Object invoke(Object reader, Object colon, Object opts) {
+ PushbackReader r = (PushbackReader) reader;
+
+ // Read ns symbol
+ Object sym = read(r, true, null, false, opts);
+ if (!(sym instanceof Symbol) || ((Symbol)sym).getNamespace() != null)
+ throw new RuntimeException("Namespaced map must specify a valid namespace: " + sym);
+ String ns = ((Symbol)sym).getName();
+
+ // Read map
+ int nextChar = read1(r);
+ while(isWhitespace(nextChar))
+ nextChar = read1(r);
+ if('{' != nextChar)
+ throw new RuntimeException("Namespaced map must specify a map");
+ List kvs = readDelimitedList('}', r, true, opts);
+ if((kvs.size() & 1) == 1)
+ throw Util.runtimeException("Namespaced map literal must contain an even number of forms");
+
+ // Construct output map
+ Object[] a = new Object[kvs.size()];
+ Iterator iter = kvs.iterator();
+ for(int i = 0; iter.hasNext(); i += 2) {
+ Object key = iter.next();
+ Object val = iter.next();
+
+ if(key instanceof Keyword) {
+ Keyword kw = (Keyword) key;
+ if (kw.getNamespace() == null) {
+ key = Keyword.intern(ns, kw.getName());
+ } else if (kw.getNamespace().equals("_")) {
+ key = Keyword.intern(null, kw.getName());
+ }
+ } else if(key instanceof Symbol) {
+ Symbol s = (Symbol) key;
+ if (s.getNamespace() == null) {
+ key = Symbol.intern(ns, s.getName());
+ } else if (s.getNamespace().equals("_")) {
+ key = Symbol.intern(null, s.getName());
+ }
+ }
+ a[i] = key;
+ a[i+1] = val;
+ }
+ return RT.map(a);
+ }
+}
+
public static class DispatchReader extends AFn{
public Object invoke(Object reader, Object hash, Object opts) {
int ch = read1((Reader) reader);
@@ -654,6 +706,26 @@ public Object invoke(Object reader, Object leftangle, Object opts) {
}
}
+
+public static class SymbolicValueReader extends AFn{
+
+ static IPersistentMap specials = PersistentHashMap.create(Symbol.intern("Inf"), Double.POSITIVE_INFINITY,
+ Symbol.intern("-Inf"), Double.NEGATIVE_INFINITY,
+ Symbol.intern("NaN"), Double.NaN);
+
+ public Object invoke(Object reader, Object quote, Object opts) {
+ PushbackReader r = (PushbackReader) reader;
+ Object o = read(r, true, null, true, opts);
+
+ if (!(o instanceof Symbol))
+ throw Util.runtimeException("Invalid token: ##" + o);
+ if (!(specials.containsKey(o)))
+ throw Util.runtimeException("Unknown symbolic value: ##" + o);
+
+ return specials.valAt(o);
+ }
+}
+
public static List readDelimitedList(char delim, PushbackReader r, boolean isRecursive, Object opts) {
final int firstline =
(r instanceof LineNumberingPushbackReader) ?
@@ -734,4 +806,3 @@ private Object readTagged(PushbackReader reader, Symbol tag, IPersistentMap opts
}
}
-
diff --git a/src/jvm/clojure/lang/IAtom2.java b/src/jvm/clojure/lang/IAtom2.java
new file mode 100644
index 00000000..ab7c0f4b
--- /dev/null
+++ b/src/jvm/clojure/lang/IAtom2.java
@@ -0,0 +1,23 @@
+/**
+ * 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;
+
+public interface IAtom2 extends IAtom {
+IPersistentVector swapVals(IFn f);
+
+IPersistentVector swapVals(IFn f, Object arg);
+
+IPersistentVector swapVals(IFn f, Object arg1, Object arg2);
+
+IPersistentVector swapVals(IFn f, Object x, Object y, ISeq args);
+
+IPersistentVector resetVals(Object newv);
+}
diff --git a/src/jvm/clojure/lang/ITransientAssociative2.java b/src/jvm/clojure/lang/ITransientAssociative2.java
new file mode 100644
index 00000000..6affcf96
--- /dev/null
+++ b/src/jvm/clojure/lang/ITransientAssociative2.java
@@ -0,0 +1,16 @@
+/**
+ * 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;
+
+public interface ITransientAssociative2 extends ITransientAssociative {
+ boolean containsKey(Object key);
+ IMapEntry entryAt(Object key);
+}
diff --git a/src/jvm/clojure/lang/LispReader.java b/src/jvm/clojure/lang/LispReader.java
index 434fec80..1860fa4e 100644
--- a/src/jvm/clojure/lang/LispReader.java
+++ b/src/jvm/clojure/lang/LispReader.java
@@ -31,6 +31,7 @@
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -106,6 +107,7 @@ public class LispReader{
dispatchMacros['^'] = new MetaReader();
+ dispatchMacros['#'] = new SymbolicValueReader();
dispatchMacros['\''] = new VarReader();
dispatchMacros['"'] = new RegexReader();
dispatchMacros['('] = new FnReader();
@@ -115,8 +117,16 @@ public class LispReader{
dispatchMacros['<'] = new UnreadableReader();
dispatchMacros['_'] = new DiscardReader();
dispatchMacros['?'] = new ConditionalReader();
+ dispatchMacros[':'] = new NamespaceMapReader();
}
+public static interface Resolver{
+ Symbol currentNS();
+ Symbol resolveClass(Symbol sym);
+ Symbol resolveAlias(Symbol sym);
+ Symbol resolveVar(Symbol sym);
+}
+
static boolean isWhitespace(int ch){
return Character.isWhitespace(ch) || ch == ',';
}
@@ -193,11 +203,11 @@ static public Object read(PushbackReader r, boolean eofIsError, Object eofValue,
static public Object read(PushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive, Object opts)
{
// start with pendingForms null as reader conditional splicing is not allowed at top level
- return read(r, eofIsError, eofValue, null, null, isRecursive, opts, null);
+ return read(r, eofIsError, eofValue, null, null, isRecursive, opts, null, (Resolver) RT.READER_RESOLVER.deref());
}
static private Object read(PushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive, Object opts, Object pendingForms) {
- return read(r, eofIsError, eofValue, null, null, isRecursive, opts, ensurePending(pendingForms));
+ return read(r, eofIsError, eofValue, null, null, isRecursive, opts, ensurePending(pendingForms), (Resolver) RT.READER_RESOLVER.deref());
}
static private Object ensurePending(Object pendingForms) {
@@ -220,7 +230,9 @@ static private Object installPlatformFeature(Object opts) {
}
}
-static private Object read(PushbackReader r, boolean eofIsError, Object eofValue, Character returnOn, Object returnOnValue, boolean isRecursive, Object opts, Object pendingForms)
+static private Object read(PushbackReader r, boolean eofIsError, Object eofValue, Character returnOn,
+ Object returnOnValue, boolean isRecursive, Object opts, Object pendingForms,
+ Resolver resolver)
{
if(RT.READEVAL.deref() == UNKNOWN)
throw Util.runtimeException("Reading disallowed - *read-eval* bound to :unknown");
@@ -280,7 +292,7 @@ static private Object read(PushbackReader r, boolean eofIsError, Object eofValue
}
String token = readToken(r, (char) ch);
- return interpretToken(token);
+ return interpretToken(token, resolver);
}
}
catch(Exception e)
@@ -368,7 +380,7 @@ static private int readUnicodeChar(PushbackReader r, int initch, int base, int l
return uc;
}
-static private Object interpretToken(String s) {
+static private Object interpretToken(String s, Resolver resolver) {
if(s.equals("nil"))
{
return null;
@@ -383,7 +395,7 @@ else if(s.equals("false"))
}
Object ret = null;
- ret = matchSymbol(s);
+ ret = matchSymbol(s, resolver);
if(ret != null)
return ret;
@@ -391,7 +403,7 @@ else if(s.equals("false"))
}
-private static Object matchSymbol(String s){
+private static Object matchSymbol(String s, Resolver resolver){
Matcher m = symbolPat.matcher(s);
if(m.matches())
{
@@ -405,17 +417,33 @@ private static Object matchSymbol(String s){
if(s.startsWith("::"))
{
Symbol ks = Symbol.intern(s.substring(2));
- Namespace kns;
- if(ks.ns != null)
- kns = Compiler.namespaceFor(ks);
- else
- kns = Compiler.currentNS();
- //auto-resolving keyword
- if (kns != null)
- return Keyword.intern(kns.name.name,ks.name);
- else
- return null;
- }
+ if(resolver != null)
+ {
+ Symbol nsym;
+ if(ks.ns != null)
+ nsym = resolver.resolveAlias(Symbol.intern(ks.ns));
+ else
+ nsym = resolver.currentNS();
+ //auto-resolving keyword
+ if(nsym != null)
+ return Keyword.intern(nsym.name, ks.name);
+ else
+ return null;
+ }
+ else
+ {
+ Namespace kns;
+ if(ks.ns != null)
+ kns = Compiler.currentNS().lookupAlias(Symbol.intern(ks.ns));
+ else
+ kns = Compiler.currentNS();
+ //auto-resolving keyword
+ if(kns != null)
+ return Keyword.intern(kns.name.name, ks.name);
+ else
+ return null;
+ }
+ }
boolean isKeyword = s.charAt(0) == ':';
Symbol sym = Symbol.intern(s.substring(isKeyword ? 1 : 0));
if(isKeyword)
@@ -597,6 +625,130 @@ public Object invoke(Object reader, Object underscore, Object opts, Object pendi
}
}
+// :a.b{:c 1} => {:a.b/c 1}
+// ::{:c 1} => {:a.b/c 1} (where *ns* = a.b)
+// ::a{:c 1} => {:a.b/c 1} (where a is aliased to a.b)
+public static class NamespaceMapReader extends AFn{
+ public Object invoke(Object reader, Object colon, Object opts, Object pendingForms) {
+ PushbackReader r = (PushbackReader) reader;
+
+ boolean auto = false;
+ int autoChar = read1(r);
+ if(autoChar == ':')
+ auto = true;
+ else
+ unread(r, autoChar);
+
+ Object sym = null;
+ int nextChar = read1(r);
+ if(isWhitespace(nextChar)) { // the #:: { } case or an error
+ if(auto) {
+ while (isWhitespace(nextChar))
+ nextChar = read1(r);
+ if(nextChar != '{') {
+ unread(r, nextChar);
+ throw Util.runtimeException("Namespaced map must specify a namespace");
+ }
+ } else {
+ unread(r, nextChar);
+ throw Util.runtimeException("Namespaced map must specify a namespace");
+ }
+ } else if(nextChar != '{') { // #:foo { } or #::foo { }
+ unread(r, nextChar);
+ sym = read(r, true, null, false, opts, pendingForms);
+ nextChar = read1(r);
+ while(isWhitespace(nextChar))
+ nextChar = read1(r);
+ }
+ if(nextChar != '{')
+ throw Util.runtimeException("Namespaced map must specify a map");
+
+ // Resolve autoresolved ns
+ String ns;
+ if (auto) {
+ Resolver resolver = (Resolver) RT.READER_RESOLVER.deref();
+ if (sym == null) {
+ if(resolver != null)
+ ns = resolver.currentNS().name;
+ else
+ ns = Compiler.currentNS().getName().getName();
+ } else if (!(sym instanceof Symbol) || ((Symbol)sym).getNamespace() != null) {
+ throw Util.runtimeException("Namespaced map must specify a valid namespace: " + sym);
+ } else {
+ Symbol resolvedNS;
+ if (resolver != null)
+ resolvedNS = resolver.resolveAlias((Symbol) sym);
+ else{
+ Namespace rns = Compiler.currentNS().lookupAlias((Symbol)sym);
+ resolvedNS = rns != null?rns.getName():null;
+ }
+
+ if(resolvedNS == null) {
+ throw Util.runtimeException("Unknown auto-resolved namespace alias: " + sym);
+ } else {
+ ns = resolvedNS.getName();
+ }
+ }
+ } else if (!(sym instanceof Symbol) || ((Symbol)sym).getNamespace() != null) {
+ throw Util.runtimeException("Namespaced map must specify a valid namespace: " + sym);
+ } else {
+ ns = ((Symbol)sym).getName();
+ }
+
+ // Read map
+ List kvs = readDelimitedList('}', r, true, opts, ensurePending(pendingForms));
+ if((kvs.size() & 1) == 1)
+ throw Util.runtimeException("Namespaced map literal must contain an even number of forms");
+
+ // Construct output map
+ Object[] a = new Object[kvs.size()];
+ Iterator iter = kvs.iterator();
+ for(int i = 0; iter.hasNext(); i += 2) {
+ Object key = iter.next();
+ Object val = iter.next();
+
+ if(key instanceof Keyword) {
+ Keyword kw = (Keyword) key;
+ if (kw.getNamespace() == null) {
+ key = Keyword.intern(ns, kw.getName());
+ } else if (kw.getNamespace().equals("_")) {
+ key = Keyword.intern(null, kw.getName());
+ }
+ } else if(key instanceof Symbol) {
+ Symbol s = (Symbol) key;
+ if (s.getNamespace() == null) {
+ key = Symbol.intern(ns, s.getName());
+ } else if (s.getNamespace().equals("_")) {
+ key = Symbol.intern(null, s.getName());
+ }
+ }
+ a[i] = key;
+ a[i+1] = val;
+ }
+ return RT.map(a);
+ }
+}
+
+
+public static class SymbolicValueReader extends AFn{
+
+ static IPersistentMap specials = PersistentHashMap.create(Symbol.intern("Inf"), Double.POSITIVE_INFINITY,
+ Symbol.intern("-Inf"), Double.NEGATIVE_INFINITY,
+ Symbol.intern("NaN"), Double.NaN);
+
+ public Object invoke(Object reader, Object quote, Object opts, Object pendingForms) {
+ PushbackReader r = (PushbackReader) reader;
+ Object o = read(r, true, null, true, opts, ensurePending(pendingForms));
+
+ if (!(o instanceof Symbol))
+ throw Util.runtimeException("Invalid token: ##" + o);
+ if (!(specials.containsKey(o)))
+ throw Util.runtimeException("Unknown symbolic value: ##" + o);
+
+ return specials.valAt(o);
+ }
+}
+
public static class WrappingReader extends AFn{
final Symbol sym;
@@ -760,7 +912,7 @@ public Object invoke(Object reader, Object pct, Object opts, Object pendingForms
PushbackReader r = (PushbackReader) reader;
if(ARG_ENV.deref() == null)
{
- return interpretToken(readToken(r, '%'));
+ return interpretToken(readToken(r, '%'), null);
}
int ch = read1(r);
unread(r, ch);
@@ -845,6 +997,7 @@ static Object syntaxQuote(Object form) {
ret = RT.list(Compiler.QUOTE, form);
else if(form instanceof Symbol)
{
+ Resolver resolver = (Resolver) RT.READER_RESOLVER.deref();
Symbol sym = (Symbol) form;
if(sym.ns == null && sym.name.endsWith("#"))
{
@@ -861,13 +1014,43 @@ else if(form instanceof Symbol)
else if(sym.ns == null && sym.name.endsWith("."))
{
Symbol csym = Symbol.intern(null, sym.name.substring(0, sym.name.length() - 1));
- csym = Compiler.resolveSymbol(csym);
+ if(resolver != null){
+ Symbol rc = resolver.resolveClass(csym);
+ if(rc != null)
+ csym = rc;
+ }
+ else
+ csym = Compiler.resolveSymbol(csym);
sym = Symbol.intern(null, csym.name.concat("."));
}
else if(sym.ns == null && sym.name.startsWith("."))
{
// Simply quote method names.
}
+ else if(resolver != null)
+ {
+ Symbol nsym = null;
+ if(sym.ns != null){
+ Symbol alias = Symbol.intern(null, sym.ns);
+ nsym = resolver.resolveClass(alias);
+ if(nsym == null)
+ nsym = resolver.resolveAlias(alias);
+ }
+ if(nsym != null){
+ // Classname/foo -> package.qualified.Classname/foo
+ sym = Symbol.intern(nsym.name, sym.name);
+ }
+ else if(sym.ns == null){
+ Symbol rsym = resolver.resolveClass(sym);
+ if(rsym == null)
+ rsym = resolver.resolveVar(sym);
+ if(rsym != null)
+ sym = rsym;
+ else
+ sym = Symbol.intern(resolver.currentNS().name,sym.name);
+ }
+ //leave alone if qualified
+ }
else
{
Object maybeClass = null;
@@ -1194,10 +1377,12 @@ public static List readDelimitedList(char delim, PushbackReader r, boolean isRec
((LineNumberingPushbackReader) r).getLineNumber() : -1;
ArrayList a = new ArrayList();
+ Resolver resolver = (Resolver) RT.READER_RESOLVER.deref();
for(; ;) {
- Object form = read(r, false, READ_EOF, delim, READ_FINISHED, isRecursive, opts, pendingForms);
+ Object form = read(r, false, READ_EOF, delim, READ_FINISHED, isRecursive, opts, pendingForms,
+ resolver);
if (form == READ_EOF) {
if (firstline < 0)
@@ -1343,7 +1528,7 @@ public static Object readCondDelimited(PushbackReader r, boolean splicing, Objec
for(; ;) {
if(result == READ_STARTED) {
// Read the next feature
- form = read(r, false, READ_EOF, ')', READ_FINISHED, true, opts, pendingForms);
+ form = read(r, false, READ_EOF, ')', READ_FINISHED, true, opts, pendingForms, null);
if (form == READ_EOF) {
if (firstline < 0)
@@ -1361,7 +1546,7 @@ public static Object readCondDelimited(PushbackReader r, boolean splicing, Objec
//Read the form corresponding to the feature, and assign it to result if everything is kosher
- form = read(r, false, READ_EOF, ')', READ_FINISHED, true, opts, pendingForms);
+ form = read(r, false, READ_EOF, ')', READ_FINISHED, true, opts, pendingForms, (Resolver) RT.READER_RESOLVER.deref());
if (form == READ_EOF) {
if (firstline < 0)
@@ -1382,7 +1567,7 @@ public static Object readCondDelimited(PushbackReader r, boolean splicing, Objec
// When we already have a result, or when the feature didn't match, discard the next form in the reader
try {
Var.pushThreadBindings(RT.map(RT.SUPPRESS_READ, RT.T));
- form = read(r, false, READ_EOF, ')', READ_FINISHED, true, opts, pendingForms);
+ form = read(r, false, READ_EOF, ')', READ_FINISHED, true, opts, pendingForms, (Resolver) RT.READER_RESOLVER.deref());
if (form == READ_EOF) {
if (firstline < 0)
diff --git a/src/jvm/clojure/lang/LongRange.java b/src/jvm/clojure/lang/LongRange.java
index 90629d90..348e3620 100644
--- a/src/jvm/clojure/lang/LongRange.java
+++ b/src/jvm/clojure/lang/LongRange.java
@@ -128,8 +128,8 @@ public void forceChunk() {
if (count > CHUNK_SIZE) { // not last chunk
long nextStart = start + (step * CHUNK_SIZE); // cannot overflow, must be < end
- _chunk = new LongChunk(start, step, CHUNK_SIZE);
_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
}
diff --git a/src/jvm/clojure/lang/Numbers.java b/src/jvm/clojure/lang/Numbers.java
index 623743bc..0440aebd 100644
--- a/src/jvm/clojure/lang/Numbers.java
+++ b/src/jvm/clojure/lang/Numbers.java
@@ -153,6 +153,11 @@ static public Number multiplyP(Object x, Object y){
}
static public Number divide(Object x, Object y){
+ if (isNaN(x)){
+ return (Number)x;
+ } else if(isNaN(y)){
+ return (Number)y;
+ }
Ops yops = ops(y);
if(yops.isZero((Number)y))
throw new ArithmeticException("Divide by zero");
@@ -1033,21 +1038,18 @@ else if(xc == BigDecimal.class)
}
@WarnBoxedMath(false)
-static int hasheq(Number x){
- Class xc = x.getClass();
-
- if(xc == Long.class
- || xc == Integer.class
- || xc == Short.class
- || xc == Byte.class
- || (xc == BigInteger.class && lte(x, Long.MAX_VALUE) && gte(x,Long.MIN_VALUE)))
- {
+static int hasheqFrom(Number x, Class xc){
+ if(xc == Integer.class
+ || xc == Short.class
+ || xc == Byte.class
+ || (xc == BigInteger.class && lte(x, Long.MAX_VALUE) && gte(x,Long.MIN_VALUE)))
+ {
long lpart = x.longValue();
return Murmur3.hashLong(lpart);
//return (int) (lpart ^ (lpart >>> 32));
- }
+ }
if(xc == BigDecimal.class)
- {
+ {
// stripTrailingZeros() to make all numerically equal
// BigDecimal values come out the same before calling
// hashCode. Special check for 0 because
@@ -1056,14 +1058,37 @@ static int hasheq(Number x){
if (isZero(x))
return BigDecimal.ZERO.hashCode();
else
- {
+ {
BigDecimal tmp = ((BigDecimal) x).stripTrailingZeros();
return tmp.hashCode();
- }
}
+ }
+ if(xc == Float.class && x.equals(-0.0f))
+ {
+ return 0; // match 0.0f
+ }
return x.hashCode();
}
+@WarnBoxedMath(false)
+static int hasheq(Number x){
+ Class xc = x.getClass();
+
+ if(xc == Long.class)
+ {
+ long lpart = x.longValue();
+ return Murmur3.hashLong(lpart);
+ //return (int) (lpart ^ (lpart >>> 32));
+ }
+ if(xc == Double.class)
+ {
+ if(x.equals(-0.0))
+ return 0; // match 0.0
+ return x.hashCode();
+ }
+ return hasheqFrom(x, xc);
+}
+
static Category category(Object x){
Class xc = x.getClass();
diff --git a/src/jvm/clojure/lang/PersistentQueue.java b/src/jvm/clojure/lang/PersistentQueue.java
index 86615ef7..9be6006a 100644
--- a/src/jvm/clojure/lang/PersistentQueue.java
+++ b/src/jvm/clojure/lang/PersistentQueue.java
@@ -31,8 +31,8 @@ public class PersistentQueue extends Obj implements IPersistentList, Collection,
final ISeq f;
final PersistentVector r;
//static final int INITIAL_REAR_SIZE = 4;
-int _hash = -1;
-int _hasheq = -1;
+int _hash;
+int _hasheq;
PersistentQueue(IPersistentMap meta, int cnt, ISeq f, PersistentVector r){
super(meta);
@@ -70,30 +70,32 @@ public boolean equals(Object obj){
}
public int hashCode(){
- if(_hash == -1)
+ int hash = this._hash;
+ if(hash == 0)
{
- int hash = 1;
+ hash = 1;
for(ISeq s = seq(); s != null; s = s.next())
{
hash = 31 * hash + (s.first() == null ? 0 : s.first().hashCode());
}
this._hash = hash;
}
- return _hash;
+ return hash;
}
public int hasheq() {
- if(_hasheq == -1)
- {
+ int cached = this._hasheq;
+ if(cached == 0)
+ {
// int hash = 1;
// for(ISeq s = seq(); s != null; s = s.next())
// {
// hash = 31 * hash + Util.hasheq(s.first());
// }
// this._hasheq = hash;
- _hasheq = Murmur3.hashOrdered(this);
+ this._hasheq = cached = Murmur3.hashOrdered(this);
}
- return _hasheq;
+ return cached;
}
public Object peek(){
diff --git a/src/jvm/clojure/lang/PersistentTreeMap.java b/src/jvm/clojure/lang/PersistentTreeMap.java
index adbbb973..7c792bb4 100644
--- a/src/jvm/clojure/lang/PersistentTreeMap.java
+++ b/src/jvm/clojure/lang/PersistentTreeMap.java
@@ -94,6 +94,22 @@ public boolean containsKey(Object key){
return entryAt(key) != null;
}
+public boolean equals(Object obj){
+ try {
+ return super.equals(obj);
+ } catch (ClassCastException e) {
+ return false;
+ }
+}
+
+public boolean equiv(Object obj){
+ try {
+ return super.equiv(obj);
+ } catch (ClassCastException e) {
+ return false;
+ }
+}
+
public PersistentTreeMap assocEx(Object key, Object val) {
Box found = new Box(null);
Node t = add(tree, key, val, found);
diff --git a/src/jvm/clojure/lang/PersistentTreeSet.java b/src/jvm/clojure/lang/PersistentTreeSet.java
index a5dc8ec6..4a112692 100644
--- a/src/jvm/clojure/lang/PersistentTreeSet.java
+++ b/src/jvm/clojure/lang/PersistentTreeSet.java
@@ -42,6 +42,22 @@ static public PersistentTreeSet create(Comparator comp, ISeq items){
this._meta = meta;
}
+public boolean equals(Object obj){
+ try {
+ return super.equals(obj);
+ } catch (ClassCastException e) {
+ return false;
+ }
+}
+
+public boolean equiv(Object obj){
+ try {
+ return super.equiv(obj);
+ } catch (ClassCastException e) {
+ return false;
+ }
+}
+
public IPersistentSet disjoin(Object key) {
if(contains(key))
return new PersistentTreeSet(meta(),impl.without(key));
diff --git a/src/jvm/clojure/lang/PersistentVector.java b/src/jvm/clojure/lang/PersistentVector.java
index 3f6de59a..459dfb57 100644
--- a/src/jvm/clojure/lang/PersistentVector.java
+++ b/src/jvm/clojure/lang/PersistentVector.java
@@ -515,7 +515,7 @@ else if(subidx == 0)
}
}
-static final class TransientVector extends AFn implements ITransientVector, Counted{
+static final class TransientVector extends AFn implements ITransientVector, ITransientAssociative2, Counted{
volatile int cnt;
volatile int shift;
volatile Node root;
@@ -678,6 +678,18 @@ public Object valAt(Object key, Object notFound){
return notFound;
}
+ private static final Object NOT_FOUND = new Object();
+ public final boolean containsKey(Object key){
+ return valAt(key, NOT_FOUND) != NOT_FOUND;
+ }
+
+ public final IMapEntry entryAt(Object key){
+ Object v = valAt(key, NOT_FOUND);
+ if(v != NOT_FOUND)
+ return MapEntry.create(key, v);
+ return null;
+ }
+
public Object invoke(Object arg1) {
//note - relies on ensureEditable in nth
if(Util.isInteger(arg1))
diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java
index 9f3c177c..dd450aa2 100644
--- a/src/jvm/clojure/lang/RT.java
+++ b/src/jvm/clojure/lang/RT.java
@@ -36,7 +36,7 @@ public class RT{
static final public String LOADER_SUFFIX = "__init";
//simple-symbol->class
-final static IPersistentMap DEFAULT_IMPORTS = map(
+final static public IPersistentMap DEFAULT_IMPORTS = map(
// Symbol.intern("RT"), "clojure.lang.RT",
// Symbol.intern("Num"), "clojure.lang.Num",
// Symbol.intern("Symbol"), "clojure.lang.Symbol",
@@ -227,6 +227,7 @@ else if(s.equals("false"))
final static Var PRINT_DUP = Var.intern(CLOJURE_NS, Symbol.intern("*print-dup*"), F).setDynamic();
final static Var WARN_ON_REFLECTION = Var.intern(CLOJURE_NS, Symbol.intern("*warn-on-reflection*"), F).setDynamic();
final static Var ALLOW_UNRESOLVED_VARS = Var.intern(CLOJURE_NS, Symbol.intern("*allow-unresolved-vars*"), F).setDynamic();
+final static Var READER_RESOLVER = Var.intern(CLOJURE_NS, Symbol.intern("*reader-resolver*"), null).setDynamic();
final static Var IN_NS_VAR = Var.intern(CLOJURE_NS, Symbol.intern("in-ns"), F);
final static Var NS_VAR = Var.intern(CLOJURE_NS, Symbol.intern("ns"), F);
@@ -298,6 +299,10 @@ static public void addURL(Object url) throws MalformedURLException{
throw new IllegalAccessError("Context classloader is not a DynamicClassLoader");
}
+public static boolean checkSpecAsserts = Boolean.getBoolean("clojure.spec.check-asserts");
+public static boolean instrumentMacros = ! Boolean.getBoolean("clojure.spec.skip-macros");
+static volatile boolean CHECK_SPECS = false;
+
static{
Keyword arglistskw = Keyword.intern(null, "arglists");
Symbol namesym = Symbol.intern("name");
@@ -333,6 +338,8 @@ public Object invoke(Object arg1) {
catch(Exception e) {
throw Util.sneakyThrow(e);
}
+
+ CHECK_SPECS = RT.instrumentMacros;
}
static public Keyword keyword(String ns, String name){
@@ -523,6 +530,7 @@ else if(coll instanceof LazySeq)
return seqFrom(coll);
}
+// N.B. canSeq must be kept in sync with this!
static ISeq seqFrom(Object coll){
if(coll instanceof Seqable)
return ((Seqable) coll).seq();
@@ -543,6 +551,16 @@ else if(coll instanceof Map)
}
}
+static public boolean canSeq(Object coll){
+ return coll instanceof ISeq
+ || coll instanceof Seqable
+ || coll == null
+ || coll instanceof Iterable
+ || coll.getClass().isArray()
+ || coll instanceof CharSequence
+ || coll instanceof Map;
+}
+
static public Iterator iter(Object coll){
if(coll instanceof Iterable)
return ((Iterable)coll).iterator();
@@ -753,6 +771,10 @@ else if(key instanceof Number && (coll instanceof String || coll.getClass().isAr
return nth(coll, n);
return null;
}
+ else if(coll instanceof ITransientSet) {
+ ITransientSet set = (ITransientSet) coll;
+ return set.get(key);
+ }
return null;
}
@@ -782,6 +804,12 @@ else if(key instanceof Number && (coll instanceof String || coll.getClass().isAr
int n = ((Number) key).intValue();
return n >= 0 && n < count(coll) ? nth(coll, n) : notFound;
}
+ else if(coll instanceof ITransientSet) {
+ ITransientSet set = (ITransientSet) coll;
+ if(set.contains(key))
+ return set.get(key);
+ return notFound;
+ }
return notFound;
}
@@ -811,6 +839,10 @@ else if(key instanceof Number && (coll instanceof String || coll.getClass().isAr
int n = ((Number) key).intValue();
return n >= 0 && n < count(coll);
}
+ else if(coll instanceof ITransientSet)
+ return ((ITransientSet)coll).contains(key) ? T : F;
+ else if(coll instanceof ITransientAssociative2)
+ return (((ITransientAssociative2)coll).containsKey(key)) ? T : F;
throw new IllegalArgumentException("contains? not supported on type: " + coll.getClass().getName());
}
@@ -819,12 +851,16 @@ static public Object find(Object coll, Object key){
return null;
else if(coll instanceof Associative)
return ((Associative) coll).entryAt(key);
- else {
+ else if(coll instanceof Map) {
Map m = (Map) coll;
if(m.containsKey(key))
return MapEntry.create(key, m.get(key));
return null;
}
+ else if(coll instanceof ITransientAssociative2) {
+ return ((ITransientAssociative2) coll).entryAt(key);
+ }
+ throw new IllegalArgumentException("find not supported on type: " + coll.getClass().getName());
}
//takes a seq of key,val,key,val
diff --git a/src/jvm/clojure/lang/Var.java b/src/jvm/clojure/lang/Var.java
index 57c296eb..221c9044 100644
--- a/src/jvm/clojure/lang/Var.java
+++ b/src/jvm/clojure/lang/Var.java
@@ -12,10 +12,12 @@
package clojure.lang;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
import java.util.concurrent.atomic.AtomicBoolean;
-public final class Var extends ARef implements IFn, IRef, Settable{
+public final class Var extends ARef implements IFn, IRef, Settable, Serializable{
static class TBox{
@@ -698,7 +700,7 @@ public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object
}
public Object applyTo(ISeq arglist) {
- return AFn.applyToHelper(this, arglist);
+ return fn().applyTo(arglist);
}
static IFn assoc = new AFn(){
@@ -713,4 +715,28 @@ public Object invoke(Object c, Object k) {
return RT.dissoc(c, k);
}
};
+
+
+/***
+ Note - serialization only supports reconnecting the Var identity on the deserializing end
+ Neither the value in the var nor any of its properties are serialized
+***/
+
+private static class Serialized implements Serializable{
+ public Serialized(Symbol nsName, Symbol sym){
+ this.nsName = nsName;
+ this.sym = sym;
+ }
+
+ private Symbol nsName;
+ private Symbol sym;
+
+ private Object readResolve() throws ObjectStreamException{
+ return intern(nsName, sym);
+ }
+}
+
+private Object writeReplace() throws ObjectStreamException{
+ return new Serialized(ns.getName(), sym);
+}
}
diff --git a/test/clojure/test_clojure/atoms.clj b/test/clojure/test_clojure/atoms.clj
index 672a1487..f9ecadcc 100644
--- a/test/clojure/test_clojure/atoms.clj
+++ b/test/clojure/test_clojure/atoms.clj
@@ -18,3 +18,27 @@
; swap! reset!
; compare-and-set!
+(deftest swap-vals-returns-old-value
+ (let [a (atom 0)]
+ (is (= [0 1] (swap-vals! a inc)))
+ (is (= [1 2] (swap-vals! a inc)))
+ (is (= 2 @a))))
+
+(deftest deref-swap-arities
+ (binding [*warn-on-reflection* true]
+ (let [a (atom 0)]
+ (is (= [0 1] (swap-vals! a + 1)))
+ (is (= [1 3] (swap-vals! a + 1 1)))
+ (is (= [3 6] (swap-vals! a + 1 1 1)))
+ (is (= [6 10] (swap-vals! a + 1 1 1 1)))
+ (is (= 10 @a)))))
+
+(deftest deref-reset-returns-old-value
+ (let [a (atom 0)]
+ (is (= [0 :b] (reset-vals! a :b)))
+ (is (= [:b 45M] (reset-vals! a 45M)))
+ (is (= 45M @a))))
+
+(deftest reset-on-deref-reset-equality
+ (let [a (atom :usual-value)]
+ (is (= :usual-value (reset! a (first (reset-vals! a :almost-never-seen-value)))))))
diff --git a/test/clojure/test_clojure/compilation.clj b/test/clojure/test_clojure/compilation.clj
index a730b89c..11a57372 100644
--- a/test/clojure/test_clojure/compilation.clj
+++ b/test/clojure/test_clojure/compilation.clj
@@ -354,6 +354,33 @@
;; throws an exception on failure
(is (eval `(fn [] ~(CLJ1399. 1)))))
+(deftest CLJ-1250-this-clearing
+ (testing "clearing during try/catch/finally"
+ (let [closed-over-in-catch (let [x :foo]
+ (fn []
+ (try
+ (throw (Exception. "boom"))
+ (catch Exception e
+ x)))) ;; x should remain accessible to the fn
+
+ a (atom nil)
+ closed-over-in-finally (fn []
+ (try
+ :ret
+ (finally
+ (reset! a :run))))]
+ (is (= :foo (closed-over-in-catch)))
+ (is (= :ret (closed-over-in-finally)))
+ (is (= :run @a))))
+ (testing "no clearing when loop not in return context"
+ (let [x (atom 5)
+ bad (fn []
+ (loop [] (System/getProperties))
+ (swap! x dec)
+ (when (pos? @x)
+ (recur)))]
+ (is (nil? (bad))))))
+
(deftest CLJ-1586-lazyseq-literals-preserve-metadata
(should-not-reflect (eval (list '.substring (with-meta (concat '(identity) '("foo")) {:tag 'String}) 0))))
@@ -395,3 +422,9 @@
;; eventually call `load` and reset called?.
(require 'clojure.repl :reload))
(is @called?)))
+
+(deftest clj-1714
+ (testing "CLJ-1714 Classes shouldn't have their static initialisers called simply by type hinting or importing"
+ ;; ClassWithFailingStaticInitialiser will throw if its static initialiser is called
+ (is (eval '(fn [^compilation.ClassWithFailingStaticInitialiser c])))
+ (is (eval '(import (compilation ClassWithFailingStaticInitialiser))))))
diff --git a/test/clojure/test_clojure/data.clj b/test/clojure/test_clojure/data.clj
index 5a241e07..0b6e5d54 100644
--- a/test/clojure/test_clojure/data.clj
+++ b/test/clojure/test_clojure/data.clj
@@ -8,7 +8,7 @@
(ns clojure.test-clojure.data
(:use clojure.data clojure.test)
- (import java.util.HashSet))
+ (:import java.util.HashSet))
(deftest diff-test
(are [d x y] (= d (diff x y))
diff --git a/test/clojure/test_clojure/data_structures.clj b/test/clojure/test_clojure/data_structures.clj
index 9151ceb9..7e32fe5c 100644
--- a/test/clojure/test_clojure/data_structures.clj
+++ b/test/clojure/test_clojure/data_structures.clj
@@ -202,6 +202,8 @@
(hash-map :a 1 :b 2)
(array-map :a 1 :b 2))
+ (is (not= (sorted-set :a) (sorted-set 1)))
+
; sorted-set vs. hash-set
(is (not= (class (sorted-set 1)) (class (hash-set 1))))
(are [x y] (= x y)
@@ -1287,4 +1289,17 @@
(defspec seq-and-iter-match-for-structs
identity
[^{:tag clojure.test-clojure.data-structures/gen-struct} s]
- (seq-iter-match s s))
\ No newline at end of file
+ (seq-iter-match s s))
+
+(deftest record-hashing
+ (let [r (->Rec 1 1)
+ _ (hash r)
+ r2 (assoc r :c 2)]
+ (is (= (hash (->Rec 1 1)) (hash r)))
+ (is (= (hash r) (hash (with-meta r {:foo 2}))))
+ (is (not= (hash (->Rec 1 1)) (hash (assoc (->Rec 1 1) :a 2))))
+ (is (not= (hash (->Rec 1 1)) (hash r2)))
+ (is (not= (hash (->Rec 1 1)) (hash (assoc r :a 2))))
+ (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))))))
diff --git a/test/clojure/test_clojure/def.clj b/test/clojure/test_clojure/def.clj
index a01489aa..7ea442ef 100644
--- a/test/clojure/test_clojure/def.clj
+++ b/test/clojure/test_clojure/def.clj
@@ -13,34 +13,34 @@
(deftest defn-error-messages
(testing "multiarity syntax invalid parameter declaration"
(is (fails-with-cause?
- IllegalArgumentException
- #"Parameter declaration \"arg1\" should be a vector"
+ clojure.lang.ExceptionInfo
+ #"Call to clojure.core/defn did not conform to spec"
(eval-in-temp-ns (defn foo (arg1 arg2))))))
(testing "multiarity syntax invalid signature"
(is (fails-with-cause?
- IllegalArgumentException
- #"Invalid signature \"\[a b\]\" should be a list"
+ clojure.lang.ExceptionInfo
+ #"Call to clojure.core/defn did not conform to spec"
(eval-in-temp-ns (defn foo
([a] 1)
[a b])))))
(testing "assume single arity syntax"
(is (fails-with-cause?
- IllegalArgumentException
- #"Parameter declaration \"a\" should be a vector"
+ clojure.lang.ExceptionInfo
+ #"Call to clojure.core/defn did not conform to spec"
(eval-in-temp-ns (defn foo a)))))
(testing "bad name"
(is (fails-with-cause?
- IllegalArgumentException
- #"First argument to defn must be a symbol"
+ clojure.lang.ExceptionInfo
+ #"Call to clojure.core/defn did not conform to spec"
(eval-in-temp-ns (defn "bad docstring" testname [arg1 arg2])))))
(testing "missing parameter/signature"
(is (fails-with-cause?
- IllegalArgumentException
- #"Parameter declaration missing"
+ clojure.lang.ExceptionInfo
+ #"Call to clojure.core/defn did not conform to spec"
(eval-in-temp-ns (defn testname)))))
(testing "allow trailing map"
@@ -48,8 +48,8 @@
(testing "don't allow interleaved map"
(is (fails-with-cause?
- IllegalArgumentException
- #"Invalid signature \"\{:a :b\}\" should be a list"
+ clojure.lang.ExceptionInfo
+ #"Call to clojure.core/defn did not conform to spec"
(eval-in-temp-ns (defn a "asdf" ([a] 1) {:a :b} ([] 1)))))))
(deftest non-dynamic-warnings
diff --git a/test/clojure/test_clojure/delays.clj b/test/clojure/test_clojure/delays.clj
index 0de33410..0a2a1c99 100644
--- a/test/clojure/test_clojure/delays.clj
+++ b/test/clojure/test_clojure/delays.clj
@@ -1,5 +1,6 @@
(ns clojure.test-clojure.delays
- (:use clojure.test))
+ (:use clojure.test)
+ (:import [java.util.concurrent CyclicBarrier]))
(deftest calls-once
(let [a (atom 0)
@@ -9,6 +10,27 @@
(is (= 1 @d))
(is (= 1 @a))))
+(deftest calls-once-in-parallel
+ (let [a (atom 0)
+ d (delay (swap! a inc))
+ threads 100
+ ^CyclicBarrier barrier (CyclicBarrier. (+ threads 1))]
+ (is (= 0 @a))
+ (dotimes [_ threads]
+ (->
+ (Thread.
+ (fn []
+ (.await barrier)
+ (dotimes [_ 10000]
+ (is (= 1 @d)))
+ (.await barrier)))
+ (.start)))
+ (.await barrier)
+ (.await barrier)
+ (is (= 1 @d))
+ (is (= 1 @d))
+ (is (= 1 @a))))
+
(deftest saves-exceptions
(let [f #(do (throw (Exception. "broken"))
1)
@@ -19,3 +41,28 @@
first-result (try-call)]
(is (instance? Exception first-result))
(is (identical? first-result (try-call)))))
+
+(deftest saves-exceptions-in-parallel
+ (let [f #(do (throw (Exception. "broken"))
+ 1)
+ d (delay (f))
+ try-call #(try
+ @d
+ (catch Exception e e))
+ threads 100
+ ^CyclicBarrier barrier (CyclicBarrier. (+ threads 1))]
+ (dotimes [_ threads]
+ (->
+ (Thread.
+ (fn []
+ (.await barrier)
+ (let [first-result (try-call)]
+ (dotimes [_ 10000]
+ (is (instance? Exception (try-call)))
+ (is (identical? first-result (try-call)))))
+ (.await barrier)))
+ (.start)))
+ (.await barrier)
+ (.await barrier)
+ (is (instance? Exception (try-call)))
+ (is (identical? (try-call) (try-call)))))
diff --git a/test/clojure/test_clojure/errors.clj b/test/clojure/test_clojure/errors.clj
index 9131f6a5..a1c82771 100644
--- a/test/clojure/test_clojure/errors.clj
+++ b/test/clojure/test_clojure/errors.clj
@@ -42,15 +42,10 @@
(refer 'clojure.core :rename '{with-open renamed-with-open})
; would have used `are` here, but :line meta on &form doesn't survive successive macroexpansions
- (doseq [[msg-regex-str form] [["if-let .* in %s:\\d+" '(if-let [a 5
- b 6]
- true nil)]
- ["let .* in %s:\\d+" '(let [a])]
- ["let .* in %s:\\d+" '(let (a))]
- ["renamed-with-open .* in %s:\\d+" '(renamed-with-open [a])]]]
+ (doseq [[msg-regex-str form] [["renamed-with-open" "(renamed-with-open [a])"]]]
(is (thrown-with-msg? IllegalArgumentException
(re-pattern (format msg-regex-str *ns*))
- (macroexpand form)))))
+ (macroexpand (read-string form))))))
(deftest extract-ex-data
(try
@@ -79,7 +74,19 @@
data-top-level :data}
(Throwable->map (ex-info "ex-info"
{:some "data"}))]
- (is (= data data-top-level {:some "data"})))))
+ (is (= data data-top-level {:some "data"}))))
+ (testing "nil stack handled"
+ (let [t (Throwable. "abc")]
+ ;; simulate what can happen when Java omits stack traces
+ (.setStackTrace t (into-array StackTraceElement []))
+ (let [{:keys [cause via trace]} (Throwable->map t)]
+ (is (= cause "abc"))
+ (is (= trace []))
+
+ ;; fail if printing throws an exception
+ (try
+ (with-out-str (pr t))
+ (catch Throwable t (is nil)))))))
(deftest ex-info-disallows-nil-data
(is (thrown? IllegalArgumentException (ex-info "message" nil)))
diff --git a/test/clojure/test_clojure/fn.clj b/test/clojure/test_clojure/fn.clj
index c85b155d..dfd1eaf2 100644
--- a/test/clojure/test_clojure/fn.clj
+++ b/test/clojure/test_clojure/fn.clj
@@ -13,43 +13,43 @@
(deftest fn-error-checking
(testing "bad arglist"
- (is (fails-with-cause? java.lang.IllegalArgumentException
- #"Parameter declaration a should be a vector"
+ (is (fails-with-cause? clojure.lang.ExceptionInfo
+ #"Call to clojure.core/fn did not conform to spec"
(eval '(fn "a" a)))))
(testing "treat first param as args"
- (is (fails-with-cause? java.lang.IllegalArgumentException
- #"Parameter declaration a should be a vector"
+ (is (fails-with-cause? clojure.lang.ExceptionInfo
+ #"Call to clojure.core/fn did not conform to spec"
(eval '(fn "a" [])))))
(testing "looks like listy signature, but malformed declaration"
- (is (fails-with-cause? java.lang.IllegalArgumentException
- #"Parameter declaration 1 should be a vector"
+ (is (fails-with-cause? clojure.lang.ExceptionInfo
+ #"Call to clojure.core/fn did not conform to spec"
(eval '(fn (1))))))
(testing "checks each signature"
- (is (fails-with-cause? java.lang.IllegalArgumentException
- #"Parameter declaration a should be a vector"
+ (is (fails-with-cause? clojure.lang.ExceptionInfo
+ #"Call to clojure.core/fn did not conform to spec"
(eval '(fn
([a] 1)
("a" 2))))))
(testing "correct name but invalid args"
- (is (fails-with-cause? java.lang.IllegalArgumentException
- #"Parameter declaration a should be a vector"
+ (is (fails-with-cause? clojure.lang.ExceptionInfo
+ #"Call to clojure.core/fn did not conform to spec"
(eval '(fn a "a")))))
(testing "first sig looks multiarity, rest of sigs should be lists"
- (is (fails-with-cause? java.lang.IllegalArgumentException
- #"Invalid signature \[a b\] should be a list"
+ (is (fails-with-cause? clojure.lang.ExceptionInfo
+ #"Call to clojure.core/fn did not conform to spec"
(eval '(fn a
([a] 1)
[a b])))))
(testing "missing parameter declaration"
- (is (fails-with-cause? java.lang.IllegalArgumentException
- #"Parameter declaration missing"
+ (is (fails-with-cause? clojure.lang.ExceptionInfo
+ #"Call to clojure.core/fn did not conform to spec"
(eval '(fn a))))
- (is (fails-with-cause? java.lang.IllegalArgumentException
- #"Parameter declaration missing"
+ (is (fails-with-cause? clojure.lang.ExceptionInfo
+ #"Call to clojure.core/fn did not conform to spec"
(eval '(fn))))))
diff --git a/test/clojure/test_clojure/java_interop.clj b/test/clojure/test_clojure/java_interop.clj
index 86ba5ca6..223d6efd 100644
--- a/test/clojure/test_clojure/java_interop.clj
+++ b/test/clojure/test_clojure/java_interop.clj
@@ -10,7 +10,8 @@
(ns clojure.test-clojure.java-interop
- (:use clojure.test))
+ (:use clojure.test)
+ (:require [clojure.inspector]))
; http://clojure.org/java_interop
; http://clojure.org/compilation
@@ -150,8 +151,10 @@
(:class b) java.awt.Color )))
(deftest test-iterable-bean
- (is (.iterator ^Iterable (bean (java.util.Date.))))
- (is (hash (bean (java.util.Date.)))))
+ (let [b (bean (java.util.Date.))]
+ (is (.iterator ^Iterable b))
+ (is (= (into [] b) (into [] (seq b))))
+ (is (hash b))))
; proxy, proxy-super
@@ -171,6 +174,34 @@
"chain chain chain")))
+;; serialized-proxy can be regenerated using a modified version of
+;; Clojure with the proxy serialization prohibition disabled and the
+;; following code:
+#_(let [baos (java.io.ByteArrayOutputStream.) ]
+ (with-open [baos baos]
+ (.writeObject (java.io.ObjectOutputStream. baos) (clojure.inspector/list-model nil)))
+ (println (apply str (for [c (String. (.toByteArray baos) "ISO-8859-1")]
+ (if (<= 32 (int c) (int \z)) c (format "\\%03o" (int c)))))))
+(def serialized-proxy "\254\355\000\005sr\000Eclojure.inspector.proxy$javax.swing.table.AbstractTableModel$ff19274art\330\266_\010ME\002\000\001L\000\016__clojureFnMapt\000\035Lclojure/lang/IPersistentMap;xr\000$javax.swing.table.AbstractTableModelr\313\3538\256\001\377\276\002\000\001L\000\014listenerListt\000%Ljavax/swing/event/EventListenerList;xpsr\000#javax.swing.event.EventListenerList\2616\306\175\204\352\326D\003\000\000xppxsr\000\037clojure.lang.PersistentArrayMap\3437p\017\230\305\364\337\002\000\002L\000\005_metaq\000\176\000\001[\000\005arrayt\000\023[Ljava/lang/Object;xr\000\033clojure.lang.APersistentMap]\174/\003t r\173\002\000\002I\000\005_hashI\000\007_hasheqxp\000\000\000\000\000\000\000\000pur\000\023[Ljava.lang.Object;\220\316X\237\020s)l\002\000\000xp\000\000\000\006t\000\016getColumnCountsr\000%clojure.inspector$list_model$fn__8816H\252\320\325b\371!+\002\000\000xr\000\026clojure.lang.AFunction>\006p\234\236F\375\313\002\000\001L\000\021__methodImplCachet\000\036Lclojure/lang/MethodImplCache;xppt\000\013getRowCountsr\000%clojure.inspector$list_model$fn__8818-\037I\247\234/U\226\002\000\001L\000\005nrowst\000\022Ljava/lang/Object;xq\000\176\000\017ppt\000\012getValueAtsr\000%clojure.inspector$list_model$fn__8820\323\331\174ke\233\370\034\002\000\002L\000\011get_labelq\000\176\000\024L\000\011get_valueq\000\176\000\024xq\000\176\000\017ppp")
+
+(deftest test-proxy-non-serializable
+ (testing "That proxy classes refuse serialization and deserialization"
+ ;; Serializable listed directly in interface list:
+ (is (thrown? java.io.NotSerializableException
+ (-> (java.io.ByteArrayOutputStream.)
+ (java.io.ObjectOutputStream.)
+ (.writeObject (proxy [Object java.io.Serializable] [])))))
+ ;; Serializable included via inheritence:
+ (is (thrown? java.io.NotSerializableException
+ (-> (java.io.ByteArrayOutputStream.)
+ (java.io.ObjectOutputStream.)
+ (.writeObject (clojure.inspector/list-model nil)))))
+ ;; Deserialization also prohibited:
+ (is (thrown? java.io.NotSerializableException
+ (-> serialized-proxy (.getBytes "ISO-8859-1")
+ java.io.ByteArrayInputStream. java.io.ObjectInputStream.
+ .readObject)))))
+
(deftest test-bases
(are [x y] (= x y)
(bases java.lang.Math)
diff --git a/test/clojure/test_clojure/numbers.clj b/test/clojure/test_clojure/numbers.clj
index f09dd4bf..c315caa5 100644
--- a/test/clojure/test_clojure/numbers.clj
+++ b/test/clojure/test_clojure/numbers.clj
@@ -72,6 +72,7 @@
(all-pairs-equal #'= [(byte 2) (short 2) (int 2) (long 2)
(bigint 2) (biginteger 2)])
(all-pairs-equal #'= [(float 2.0) (double 2.0)])
+ (all-pairs-equal #'= [(float 0.0) (double 0.0) (float -0.0) (double -0.0)])
(all-pairs-equal #'= [2.0M 2.00M])
(all-pairs-equal #'= [(float 1.5) (double 1.5)])
(all-pairs-equal #'= [1.50M 1.500M])
@@ -85,12 +86,13 @@
(bigint 2)
(double 2.0) 2.0M 2.00M])
(all-pairs-hash-consistent-with-= [(/ 3 2) (double 1.5) 1.50M 1.500M])
- (all-pairs-hash-consistent-with-= [(double 0.0) 0.0M 0.00M])
+ (all-pairs-hash-consistent-with-= [(double -0.0) (double 0.0) -0.0M -0.00M 0.0M 0.00M (float -0.0) (float 0.0)])
;; == tests for numerical equality, returning true even for numbers
;; in different categories.
(all-pairs-equal #'== [(byte 0) (short 0) (int 0) (long 0)
(bigint 0) (biginteger 0)
+ (float -0.0) (double -0.0) -0.0M -0.00M
(float 0.0) (double 0.0) 0.0M 0.00M])
(all-pairs-equal #'== [(byte 2) (short 2) (int 2) (long 2)
(bigint 2) (biginteger 2)
@@ -807,3 +809,73 @@ Math/pow overflows to Infinity."
(<= 1000 Double/NaN) (<= 1000 (Double. Double/NaN))
(> 1000 Double/NaN) (> 1000 (Double. Double/NaN))
(>= 1000 Double/NaN) (>= 1000 (Double. Double/NaN))))
+
+(deftest test-nan-as-operand
+ (testing "All numeric operations with NaN as an operand produce NaN as a result"
+ (let [nan Double/NaN
+ onan (cast Object Double/NaN)]
+ (are [x] (Double/isNaN x)
+ (+ nan 1)
+ (+ nan 0)
+ (+ nan 0.0)
+ (+ 1 nan)
+ (+ 0 nan)
+ (+ 0.0 nan)
+ (+ nan nan)
+ (- nan 1)
+ (- nan 0)
+ (- nan 0.0)
+ (- 1 nan)
+ (- 0 nan)
+ (- 0.0 nan)
+ (- nan nan)
+ (* nan 1)
+ (* nan 0)
+ (* nan 0.0)
+ (* 1 nan)
+ (* 0 nan)
+ (* 0.0 nan)
+ (* nan nan)
+ (/ nan 1)
+ (/ nan 0)
+ (/ nan 0.0)
+ (/ 1 nan)
+ (/ 0 nan)
+ (/ 0.0 nan)
+ (/ nan nan)
+ (+ onan 1)
+ (+ onan 0)
+ (+ onan 0.0)
+ (+ 1 onan)
+ (+ 0 onan)
+ (+ 0.0 onan)
+ (+ onan onan)
+ (- onan 1)
+ (- onan 0)
+ (- onan 0.0)
+ (- 1 onan)
+ (- 0 onan)
+ (- 0.0 onan)
+ (- onan onan)
+ (* onan 1)
+ (* onan 0)
+ (* onan 0.0)
+ (* 1 onan)
+ (* 0 onan)
+ (* 0.0 onan)
+ (* onan onan)
+ (/ onan 1)
+ (/ onan 0)
+ (/ onan 0.0)
+ (/ 1 onan)
+ (/ 0 onan)
+ (/ 0.0 onan)
+ (/ onan onan)
+ (+ nan onan)
+ (+ onan nan)
+ (- nan onan)
+ (- onan nan)
+ (* nan onan)
+ (* onan nan)
+ (/ nan onan)
+ (/ onan nan) ))))
diff --git a/test/clojure/test_clojure/other_functions.clj b/test/clojure/test_clojure/other_functions.clj
index f5d438d9..958df5ec 100644
--- a/test/clojure/test_clojure/other_functions.clj
+++ b/test/clojure/test_clojure/other_functions.clj
@@ -328,6 +328,19 @@
(apply (apply some-fn (repeat i (comp not boolean))) (range i))))
true))))
+
+(deftest test-max-min-key
+ (are [k coll min-item max-item] (and (= min-item (apply min-key k coll))
+ (= max-item (apply max-key k coll)))
+ count ["longest" "a" "xy" "foo" "bar"] "a" "longest"
+ - [5 10 15 20 25] 25 5
+ #(if (neg? %) (- %) %) [-2 -1 0 1 2 3 4] 0 4
+ {nil 1 false -1 true 0} [true true false nil] false nil)
+ (are [f k coll expected] (= expected (apply f k coll))
+ min-key :x [{:x 1000} {:x 1001} {:x 1002} {:x 1000 :second true}] {:x 1000 :second true}
+ max-key :x [{:x 1000} {:x 999} {:x 998} {:x 1000 :second true}] {:x 1000 :second true}))
+
+
; Printing
; pr prn print println newline
; pr-str prn-str print-str println-str [with-out-str (vars.clj)]
diff --git a/test/clojure/test_clojure/predicates.clj b/test/clojure/test_clojure/predicates.clj
index 2923ef3d..7efdc6fe 100644
--- a/test/clojure/test_clojure/predicates.clj
+++ b/test/clojure/test_clojure/predicates.clj
@@ -140,3 +140,35 @@
(are [x] (not (string? x))
(new java.lang.StringBuilder "abc")
(new java.lang.StringBuffer "xyz")))
+
+(def pred-val-table
+ (let [now (java.util.Date.)
+ uuid (java.util.UUID/randomUUID)
+ barray (byte-array 0)
+ uri (java.net.URI. "http://clojure.org")]
+ ['
+ [identity int? pos-int? neg-int? nat-int? double? boolean? indexed? seqable? ident? uuid? decimal? inst? uri? bytes?]
+ [0 true false false true false false false false false false false false false false]
+ [1 true true false true false false false false false false false false false false]
+ [-1 true false true false false false false false false false false false false false]
+ [1.0 false false false false true false false false false false false false false false]
+ [true false false false false false true false false false false false false false false]
+ [[] false false false false false false true true false false false false false false]
+ [nil false false false false false false false true false false false false false false]
+ [{} false false false false false false false true false false false false false false]
+ [:foo false false false false false false false false true false false false false false]
+ ['foo false false false false false false false false true false false false false false]
+ [0.0M false false false false false false false false false false true false false false]
+ [0N false false false false false false false false false false false false false false]
+ [uuid false false false false false false false false false true false false false false]
+ [uri false false false false false false false false false false false false true false]
+ [now false false false false false false false false false false false true false false]
+ [barray false false false false false false false true false false false false false true]]))
+
+(deftest test-preds
+ (let [[preds & rows] pred-val-table]
+ (doseq [row rows]
+ (let [v (first row)]
+ (dotimes [i (count row)]
+ (is (= ((resolve (nth preds i)) v) (nth row i))
+ (pr-str (list (nth preds i) v))))))))
diff --git a/test/clojure/test_clojure/printer.clj b/test/clojure/test_clojure/printer.clj
index 30fd2215..3d9cc65f 100644
--- a/test/clojure/test_clojure/printer.clj
+++ b/test/clojure/test_clojure/printer.clj
@@ -119,27 +119,9 @@
#'var-with-meta "#'clojure.test-clojure.printer/var-with-meta"
#'var-with-type "#'clojure.test-clojure.printer/var-with-type"))
-(defn ^:private ednize-stack-trace-element
- [^StackTraceElement ste]
- [(symbol (.getClassName ste))
- (symbol (.getMethodName ste))
- (.getFileName ste)
- (.getLineNumber ste)])
-
-(defn ^:private ednize-throwable-data
- [throwable-data]
- (-> throwable-data
- (update :via (fn [vias]
- (map (fn [via]
- (-> via
- (update :type #(symbol (.getName %)))
- (update :at ednize-stack-trace-element)))
- vias)))
- (update :trace #(map ednize-stack-trace-element %))))
-
(deftest print-throwable
(binding [*data-readers* {'error identity}]
- (are [e] (= (-> e Throwable->map ednize-throwable-data)
+ (are [e] (= (-> e Throwable->map)
(-> e pr-str read-string))
(Exception. "heyo")
(Throwable. "I can a throwable"
@@ -151,3 +133,19 @@
(Error. "less outer"
(ex-info "the root"
{:with "even" :more 'data})))))))
+
+(deftest print-ns-maps
+ (is (= "#:user{:a 1}" (binding [*print-namespace-maps* true] (pr-str {:user/a 1}))))
+ (is (= "{:user/a 1}" (binding [*print-namespace-maps* false] (pr-str {:user/a 1}))))
+ (let [date-map (bean (java.util.Date. 0))]
+ (is (= (binding [*print-namespace-maps* true] (pr-str date-map))
+ (binding [*print-namespace-maps* false] (pr-str date-map))))))
+
+(deftest print-symbol-values
+ (are [s v] (= s (pr-str v))
+ "##Inf" Double/POSITIVE_INFINITY
+ "##-Inf" Double/NEGATIVE_INFINITY
+ "##NaN" Double/NaN
+ "##Inf" Float/POSITIVE_INFINITY
+ "##-Inf" Float/NEGATIVE_INFINITY
+ "##NaN" Float/NaN))
diff --git a/test/clojure/test_clojure/reader.cljc b/test/clojure/test_clojure/reader.cljc
index 6d03590d..45083090 100644
--- a/test/clojure/test_clojure/reader.cljc
+++ b/test/clojure/test_clojure/reader.cljc
@@ -22,8 +22,10 @@
read-instant-calendar
read-instant-timestamp]])
(:require clojure.walk
+ [clojure.edn :as edn]
[clojure.test.generative :refer (defspec)]
- [clojure.test-clojure.generators :as cgen])
+ [clojure.test-clojure.generators :as cgen]
+ [clojure.edn :as edn])
(:import [clojure.lang BigInt Ratio]
java.io.File
java.util.TimeZone))
@@ -211,6 +213,10 @@
(is (instance? Double -1.0))
(is (instance? Double -1.))
+ (is (= Double/POSITIVE_INFINITY ##Inf))
+ (is (= Double/NEGATIVE_INFINITY ##-Inf))
+ (is (and (instance? Double ##NaN) (.isNaN ##NaN)))
+
; Read BigDecimal
(is (instance? BigDecimal 9223372036854775808M))
(is (instance? BigDecimal -9223372036854775809M))
@@ -713,3 +719,39 @@
(is (= 23 (read-string {:eof 23} "")))
(is (= 23 (read {:eof 23} (clojure.lang.LineNumberingPushbackReader.
(java.io.StringReader. ""))))))
+
+(require '[clojure.string :as s])
+(deftest namespaced-maps
+ (is (= #:a{1 nil, :b nil, :b/c nil, :_/d nil}
+ #:a {1 nil, :b nil, :b/c nil, :_/d nil}
+ {1 nil, :a/b nil, :b/c nil, :d nil}))
+ (is (= #::{1 nil, :a nil, :a/b nil, :_/d nil}
+ #:: {1 nil, :a nil, :a/b nil, :_/d nil}
+ {1 nil, :clojure.test-clojure.reader/a nil, :a/b nil, :d nil} ))
+ (is (= #::s{1 nil, :a nil, :a/b nil, :_/d nil}
+ #::s {1 nil, :a nil, :a/b nil, :_/d nil}
+ {1 nil, :clojure.string/a nil, :a/b nil, :d nil}))
+ (is (= (read-string "#:a{b 1 b/c 2}") {'a/b 1, 'b/c 2}))
+ (is (= (binding [*ns* (the-ns 'clojure.test-clojure.reader)] (read-string "#::{b 1, b/c 2, _/d 3}")) {'clojure.test-clojure.reader/b 1, 'b/c 2, 'd 3}))
+ (is (= (binding [*ns* (the-ns 'clojure.test-clojure.reader)] (read-string "#::s{b 1, b/c 2, _/d 3}")) {'clojure.string/b 1, 'b/c 2, 'd 3})))
+
+(deftest namespaced-map-errors
+ (are [err msg form] (thrown-with-msg? err msg (read-string form))
+ Exception #"Invalid token" "#:::"
+ Exception #"Namespaced map literal must contain an even number of forms" "#:s{1}"
+ Exception #"Namespaced map must specify a valid namespace" "#:s/t{1 2}"
+ Exception #"Unknown auto-resolved namespace alias" "#::BOGUS{1 2}"
+ Exception #"Namespaced map must specify a namespace" "#: s{:a 1}"
+ Exception #"Duplicate key: :user/a" "#::{:a 1 :a 2}"
+ Exception #"Duplicate key: user/a" "#::{a 1 a 2}"))
+
+(deftest namespaced-map-edn
+ (is (= {1 1, :a/b 2, :b/c 3, :d 4}
+ (edn/read-string "#:a{1 1, :b 2, :b/c 3, :_/d 4}")
+ (edn/read-string "#:a {1 1, :b 2, :b/c 3, :_/d 4}"))))
+
+(deftest invalid-symbol-value
+ (is (thrown-with-msg? Exception #"Invalid token" (read-string "##5")))
+ (is (thrown-with-msg? Exception #"Invalid token" (edn/read-string "##5")))
+ (is (thrown-with-msg? Exception #"Unknown symbolic value" (read-string "##Foo")))
+ (is (thrown-with-msg? Exception #"Unknown symbolic value" (edn/read-string "##Foo"))))
diff --git a/test/clojure/test_clojure/reducers.clj b/test/clojure/test_clojure/reducers.clj
index c2852ccb..a884c851 100644
--- a/test/clojure/test_clojure/reducers.clj
+++ b/test/clojure/test_clojure/reducers.clj
@@ -89,3 +89,7 @@
([ret k v] (when (= k k-fail)
(throw (IndexOutOfBoundsException.)))))
(zipmap (range test-map-count) (repeat :dummy)))))))
+
+(deftest test-closed-over-clearing
+ ;; this will throw OutOfMemory without proper reference clearing
+ (is (number? (reduce + 0 (r/map identity (range 1e8))))))
diff --git a/test/clojure/test_clojure/repl.clj b/test/clojure/test_clojure/repl.clj
index 609056b5..c7a0c41b 100644
--- a/test/clojure/test_clojure/repl.clj
+++ b/test/clojure/test_clojure/repl.clj
@@ -8,7 +8,9 @@
(deftest test-doc
(testing "with namespaces"
(is (= "clojure.pprint"
- (second (str/split-lines (with-out-str (doc clojure.pprint))))))))
+ (second (str/split-lines (with-out-str (doc clojure.pprint)))))))
+ (testing "with special cases"
+ (is (= (with-out-str (doc catch)) (with-out-str (doc try))))))
(deftest test-source
(is (= "(defn foo [])" (source-fn 'clojure.test-clojure.repl.example/foo)))
@@ -24,6 +26,8 @@
(deftest test-dir
(is (thrown? Exception (dir-fn 'non-existent-ns)))
(is (= '[bar foo] (dir-fn 'clojure.test-clojure.repl.example)))
+ (binding [*ns* (the-ns 'clojure.test-clojure.repl)]
+ (is (= (dir-fn 'clojure.string) (dir-fn 'str))))
(is (= (platform-newlines "bar\nfoo\n") (with-out-str (dir clojure.test-clojure.repl.example)))))
(deftest test-apropos
diff --git a/test/clojure/test_clojure/sequences.clj b/test/clojure/test_clojure/sequences.clj
index e3adb277..146869be 100644
--- a/test/clojure/test_clojure/sequences.clj
+++ b/test/clojure/test_clojure/sequences.clj
@@ -1058,6 +1058,24 @@
(reduce + (iterator-seq (.iterator (range 100)))) 4950
(reduce + (iterator-seq (.iterator (range 0.0 100.0 1.0)))) 4950.0 ))
+(deftest range-test
+ (let [threads 10
+ n 1000
+ r (atom (range (inc n)))
+ m (atom 0)]
+ ; Iterate through the range concurrently,
+ ; updating m to the highest seen value in the range
+ (->> (range threads)
+ (map (fn [id]
+ (future
+ (loop []
+ (when-let [r (swap! r next)]
+ (swap! m max (first r))
+ (recur))))))
+ (map deref)
+ dorun)
+ (is (= n @m))))
+
(defn unlimited-range-create [& args]
(let [[arg1 arg2 arg3] args]
(case (count args)
diff --git a/test/clojure/test_clojure/serialization.clj b/test/clojure/test_clojure/serialization.clj
index 60cd65c9..316fbd62 100644
--- a/test/clojure/test_clojure/serialization.clj
+++ b/test/clojure/test_clojure/serialization.clj
@@ -169,7 +169,7 @@
(atom nil)
(ref nil)
(agent nil)
- #'+
+ ;;#'+
;; stateful seqs
(enumeration-seq (java.util.Collections/enumeration (range 50)))
diff --git a/test/clojure/test_clojure/special.clj b/test/clojure/test_clojure/special.clj
index 9432c456..abfe1bfb 100644
--- a/test/clojure/test_clojure/special.clj
+++ b/test/clojure/test_clojure/special.clj
@@ -13,7 +13,8 @@
;;
(ns clojure.test-clojure.special
- (:use clojure.test))
+ (:use clojure.test)
+ (:require [clojure.test-helper :refer [should-not-reflect]]))
; http://clojure.org/special_forms
@@ -33,46 +34,62 @@
(is (= {} x))))
(deftest keywords-in-destructuring
- (let [{:keys [:a :b]} {:a 1 :b 2}]
- (is (= 1 a))
- (is (= 2 b))))
+ (let [m {:a 1 :b 2}]
+ (let [{:keys [:a :b]} m]
+ (is (= [1 2] [a b])))
+ (let [{:keys [:a :b :c] :or {c 3}} m]
+ (is (= [1 2 3] [a b c])))))
(deftest namespaced-keywords-in-destructuring
- (let [{:keys [:a/b :c/d]} {:a/b 1 :c/d 2}]
- (is (= 1 b))
- (is (= 2 d))))
+ (let [m {:a/b 1 :c/d 2}]
+ (let [{:keys [:a/b :c/d]} m]
+ (is (= [1 2] [b d])))
+ (let [{:keys [:a/b :c/d :e/f] :or {f 3}} m]
+ (is (= [1 2 3] [b d f])))))
(deftest namespaced-keys-in-destructuring
- (let [{:keys [a/b c/d]} {:a/b 1 :c/d 2}]
- (is (= 1 b))
- (is (= 2 d))))
+ (let [m {:a/b 1 :c/d 2}]
+ (let [{:keys [a/b c/d]} m]
+ (is (= [1 2] [b d])))
+ (let [{:keys [a/b c/d e/f] :or {f 3}} m]
+ (is (= [1 2 3] [b d f])))))
(deftest namespaced-syms-in-destructuring
- (let [{:syms [a/b c/d]} {'a/b 1 'c/d 2}]
- (is (= 1 b))
- (is (= 2 d))))
+ (let [{:syms [a/b c/d e/f] :or {f 3}} {'a/b 1 'c/d 2}]
+ (is (= [1 2 3] [b d f]))))
+
+(deftest namespaced-keys-syntax
+ (let [{:a/keys [b c d] :or {d 3}} {:a/b 1 :a/c 2}]
+ (is (= [1 2 3] [b c d]))))
+
+(deftest namespaced-syms-syntax
+ (let [{:a/syms [b c d] :or {d 3}} {'a/b 1 'a/c 2}]
+ (is (= [1 2 3] [b c d]))))
(deftest keywords-not-allowed-in-let-bindings
- (is (thrown-with-msg? Exception #"Unsupported binding form: :a"
+ (is (thrown-with-msg? Exception #"did not conform to spec"
(eval '(let [:a 1] a))))
- (is (thrown-with-msg? Exception #"Unsupported binding form: :a/b"
+ (is (thrown-with-msg? Exception #"did not conform to spec"
(eval '(let [:a/b 1] b))))
- (is (thrown-with-msg? Exception #"Unsupported binding form: :a"
+ (is (thrown-with-msg? Exception #"did not conform to spec"
(eval '(let [[:a] [1]] a))))
- (is (thrown-with-msg? Exception #"Unsupported binding form: :a/b"
+ (is (thrown-with-msg? Exception #"did not conform to spec"
(eval '(let [[:a/b] [1]] b)))))
(deftest namespaced-syms-only-allowed-in-map-destructuring
- (is (thrown-with-msg? Exception #"Can't let qualified name: a/x"
+ (is (thrown-with-msg? Exception #"did not conform to spec"
(eval '(let [a/x 1, [y] [1]] x))))
- (is (thrown-with-msg? Exception #"Can't let qualified name: a/x"
+ (is (thrown-with-msg? Exception #"did not conform to spec"
(eval '(let [[a/x] [1]] x)))))
+(deftest or-doesnt-create-bindings
+ (is (thrown-with-msg? Exception #"Unable to resolve symbol: b"
+ (eval '(let [{:keys [a] :or {b 2}} {:a 1}] [a b])))))
+
(require '[clojure.string :as s])
(deftest resolve-keyword-ns-alias-in-destructuring
- (let [{:keys [::s/x ::s/y]} {:clojure.string/x 1 :clojure.string/y 2}]
- (is (= x 1))
- (is (= y 2))))
+ (let [{:keys [::s/x ::s/y ::s/z] :or {z 3}} {:clojure.string/x 1 :clojure.string/y 2}]
+ (is (= [1 2 3] [x y z]))))
(deftest quote-with-multiple-args
(let [ex (is (thrown? clojure.lang.Compiler$CompilerException
@@ -82,3 +99,9 @@
(.getCause)
(ex-data)
(:form))))))
+
+(deftest typehints-retained-destructuring
+ (should-not-reflect
+ (defn foo
+ [{:keys [^String s]}]
+ (.indexOf s "boo"))))
\ No newline at end of file
diff --git a/test/clojure/test_clojure/transients.clj b/test/clojure/test_clojure/transients.clj
index dcc956e3..f64ba926 100644
--- a/test/clojure/test_clojure/transients.clj
+++ b/test/clojure/test_clojure/transients.clj
@@ -53,3 +53,30 @@
t2 @(future (conj! t 4))
p (persistent! t2)]
(is (= [1 2 3 4] p))))
+
+(deftest transient-lookups
+ (let [tv (transient [1 2 3])]
+ (is (= 1 (get tv 0)))
+ (is (= :foo (get tv 4 :foo)))
+ (is (= true (contains? tv 0)))
+ (is (= [0 1] (find tv 0)))
+ (is (= nil (find tv -1))))
+ (let [ts (transient #{1 2})]
+ (is (= true (contains? ts 1)))
+ (is (= false (contains? ts 99)))
+ (is (= 1 (get ts 1)))
+ (is (= nil (get ts 99))))
+ (let [tam (transient (array-map :a 1 :b 2))]
+ (is (= true (contains? tam :a)))
+ (is (= false (contains? tam :x)))
+ (is (= 1 (get tam :a)))
+ (is (= nil (get tam :x)))
+ (is (= [:a 1] (find tam :a)))
+ (is (= nil (find tam :x))))
+ (let [thm (transient (hash-map :a 1 :b 2))]
+ (is (= true (contains? thm :a)))
+ (is (= false (contains? thm :x)))
+ (is (= 1 (get thm :a)))
+ (is (= nil (get thm :x)))
+ (is (= [:a 1] (find thm :a)))
+ (is (= nil (find thm :x)))))
diff --git a/test/clojure/test_clojure/vars.clj b/test/clojure/test_clojure/vars.clj
index b1d9e2da..6b454d2b 100644
--- a/test/clojure/test_clojure/vars.clj
+++ b/test/clojure/test_clojure/vars.clj
@@ -97,4 +97,13 @@
(is (= 2 dynamic-var))
(with-redefs [dynamic-var 3]
(is (= 2 dynamic-var))))
- (is (= 1 dynamic-var)))
\ No newline at end of file
+ (is (= 1 dynamic-var)))
+
+(defn sample [& args]
+ 0)
+
+(deftest test-vars-apply-lazily
+ (is (= 0 (deref (future (apply sample (range)))
+ 1000 :timeout)))
+ (is (= 0 (deref (future (apply #'sample (range)))
+ 1000 :timeout))))
diff --git a/test/clojure/test_clojure/vectors.clj b/test/clojure/test_clojure/vectors.clj
index d9faccb1..0bea3ff4 100644
--- a/test/clojure/test_clojure/vectors.clj
+++ b/test/clojure/test_clojure/vectors.clj
@@ -322,10 +322,11 @@
(vector-of :double)
(vector-of :char))
(testing "with invalid type argument"
- (are [x] (thrown? NullPointerException x)
+ (are [x] (thrown? IllegalArgumentException x)
(vector-of nil)
(vector-of Float/TYPE)
(vector-of 'int)
+ (vector-of :integer)
(vector-of ""))))
(testing "vector-like (vector-of :type x1 x2 x3 … xn)"
(are [vec gvec] (and (instance? clojure.core.Vec gvec)
@@ -360,7 +361,15 @@
(vector-of :int #{1 2 3 4})
(vector-of :int (sorted-set 1 2 3 4))
(vector-of :int 1 2 "3")
- (vector-of :int "1" "2" "3")))))
+ (vector-of :int "1" "2" "3")))
+ (testing "instances of IPersistentVector"
+ (are [gvec] (instance? clojure.lang.IPersistentVector gvec)
+ (vector-of :int 1 2 3)
+ (vector-of :double 1 2 3)))
+ (testing "fully implements IPersistentVector"
+ (are [gvec] (= 3 (.length gvec))
+ (vector-of :int 1 2 3)
+ (vector-of :double 1 2 3)))))
(deftest empty-vector-equality
(let [colls [[] (vector-of :long) '()]]
diff --git a/test/java/compilation/ClassWithFailingStaticInitialiser.java b/test/java/compilation/ClassWithFailingStaticInitialiser.java
new file mode 100644
index 00000000..6e6c8f7e
--- /dev/null
+++ b/test/java/compilation/ClassWithFailingStaticInitialiser.java
@@ -0,0 +1,13 @@
+package compilation;
+
+public class ClassWithFailingStaticInitialiser {
+ static {
+ // Static analysis refuses to compile a static initialiser
+ // which will always throw, so we pretend to branch. This may
+ // need updating if the static analysis gets cleverer in the
+ // future
+ if(true) {
+ throw new AssertionError("Static Initialiser was run when it shouldn't have been");
+ }
+ }
+}