diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE new file mode 100644 index 0000000000..31b549a89c --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE @@ -0,0 +1,5 @@ +Hi! This project does not accept pull requests. + +Please see the guidelines for contribution on how to file issues or provide patches: + +https://github.com/clojure/clojure/blob/master/CONTRIBUTING.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..d30437293c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,19 @@ +name: Release on demand + +on: + workflow_dispatch: + inputs: + releaseVersion: + description: "Version to release" + required: true + snapshotVersion: + description: "Snapshot version after release" + required: true + +jobs: + call-release: + uses: clojure/build.ci/.github/workflows/release.yml@master + with: + releaseVersion: ${{ github.event.inputs.releaseVersion }} + snapshotVersion: ${{ github.event.inputs.snapshotVersion }} + secrets: inherit diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml new file mode 100644 index 0000000000..24729578c1 --- /dev/null +++ b/.github/workflows/snapshot.yml @@ -0,0 +1,8 @@ +name: Snapshot on demand + +on: [workflow_dispatch] + +jobs: + call-snapshot: + uses: clojure/build.ci/.github/workflows/snapshot.yml@master + secrets: inherit diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000000..b50a9813d1 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,26 @@ +name: Test + +on: + push: + workflow_dispatch: + +jobs: + test: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] # macOS-latest, windows-latest] + java-version: ["8", "11", "17", "21"] + distribution: ["temurin", "corretto"] + profile: ["test-direct", "test-no-direct"] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - name: Set up Java + uses: actions/setup-java@v3 + with: + java-version: ${{ matrix.java-version }} + distribution: ${{ matrix.distribution }} + cache: 'maven' + - name: Build with Maven + run: mvn -ntp -B -P${{ matrix.profile }} clean test diff --git a/.gitignore b/.gitignore index 5376efaf3f..18cf4cc05c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ nbproject/private/ maven-classpath maven-classpath.properties .idea/ +*.iml diff --git a/.idea/libraries/Maven__org_clojure_data_generators_0_1_2.xml b/.idea/libraries/Maven__org_clojure_data_generators_0_1_2.xml deleted file mode 100644 index 78f485eeb9..0000000000 --- a/.idea/libraries/Maven__org_clojure_data_generators_0_1_2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_clojure_test_generative_0_4_0.xml b/.idea/libraries/Maven__org_clojure_test_generative_0_4_0.xml deleted file mode 100644 index 78fac6dfd0..0000000000 --- a/.idea/libraries/Maven__org_clojure_test_generative_0_4_0.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 06b2fc0581..84f0aee84b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1 +1,42 @@ -If you'd like to submit a patch, please follow the [contributing guidelines](http://clojure.org/contributing). +Hi! Thanks for your interest in Clojure! + +## I want to ask a question + +If you have a question about Clojure, please use the official Ask Clojure forum at https://ask.clojure.org. This forum is monitored by the Clojure maintainers. + +## I want to discuss an idea + +There are many interactive Clojure forums for discussion and you can find a list at [Clojure Discussion](https://clojure.org/community/resources#_clojure_discussion). + +## I want to file a bug / suggest an enhancement + +Please file it as a question on https://ask.clojure.org with the tag "problem" (possible bugs) or "request" (enhancements). + +## I want to provide a patch / PR + +If you would like to contribute patches, the Clojure dev process is described in detail at https://clojure.org/dev. + +In short, this process requires: + +- [Signing the Contributor Agreement](https://clojure.org/dev/contributor_agreement) +- [Requesting jira access](https://clojure.atlassian.net/servicedesk/customer/portal/1) + +This project does not accept pull requests. + +## I am looking for official documentation + +You can find official documentation on the Clojure web site: + +* Reference docs https://clojure.org/reference +* Tutorials and guides: https://clojure.org/guides +* API: https://clojure.org/api/api + +## What release should I use? + +Find the current release info here: + +https://clojure.org/releases/downloads + +A list of all releases can be found here: + +https://clojure.org/releases/downloads_older diff --git a/build.xml b/build.xml index 4d29bbf9a2..34fd65b2c6 100644 --- a/build.xml +++ b/build.xml @@ -82,6 +82,10 @@ + + + + @@ -101,6 +105,7 @@ + @@ -113,7 +118,7 @@ unless="maven.test.skip"> + value="#{clojure.test-clojure.compilation.load-ns clojure.test-clojure.ns-libs-load-later}"/> diff --git a/changes.md b/changes.md index 4df7e28540..f18b330f4a 100644 --- a/changes.md +++ b/changes.md @@ -1,5 +1,169 @@ +# Changes to Clojure in Version 1.11.1 + +* [CLJ-2701](https://clojure.atlassian.net/browse/CLJ-2701) + Pin serialVersionUID for Keyword and ArraySeq back to 1.10.3 values to retain binary serialization + +# Changes to Clojure in Version 1.11.0 + +## 1 Compatibility + +### 1.1 Security + +Because XML external entity (XXE) attacks can be used to disclose local files using file schemes or relative paths in the system identifier, `clojure.xml/parse` now disables external entity processing by default. + +See: https://owasp.org/www-community/vulnerabilities/XML_External_Entity_(XXE)_Processing + +This change disables the following SAX parser features: + +* `http://apache.org/xml/features/nonvalidating/load-external-dtd` +* `http://xml.org/sax/features/external-general-entities` +* `http://xml.org/sax/features/external-parameter-entities` + +If you rely on these features, modify your calls to `clojure.xml/parse` to explicitly +supply `startparse-sax` function as the final argument: +`(clojure.xml/parse the-string clojure.xml/startparse-sax)` +This modification also works on prior Clojure versions. + +* [CLJ-2611](http://dev.clojure.org/jira/browse/CLJ-2611) clojure.xml now disables XXE processing by default + +### 1.2 Dependencies + +Updated dependencies: + +* spec.alpha dependency to 0.3.218 - [changes](https://github.com/clojure/spec.alpha/blob/master/CHANGES.md) +* core.specs.alpha dependency to 0.2.62 - [changes](https://github.com/clojure/core.specs.alpha/blob/master/CHANGES.md) + +## 2 Features + +### 2.1 Keyword argument functions take a trailing map + +Keyword arguments are optional trailing variadic arguments of the form *akey aval bkey bval...​*. +In Clojure 1.11, functions taking keyword arguments can now be passed a map instead of or in addition +to and following the key/value pairs. When a lone map is passed, it is used for destructuring, else +a trailing map is added to the key/value pair map by `conj`. + +Also see: https://clojure.org/news/2021/03/18/apis-serving-people-and-programs + +* [CLJ-2603](https://clojure.atlassian.net/browse/CLJ-2603) Clojure keyword argument functions now also accept a map + +### 2.2 `:as-alias` in `require` + +Spec (and other libs) rely on qualified keywords as spec names. +Namespace aliasing in `ns` makes long names shorter but required namespaces to be loadable. +This change adds `:as-alias` to `require`, which is like `:as` but does not require the namespace to load. + +* [CLJ-2123](https://clojure.atlassian.net/browse/CLJ-2123) Add :as-alias option to require like :as but not load +* [CLJ-2665](https://clojure.atlassian.net/browse/CLJ-2665) Fix require with :as and :as-alias to load + +## 3 New functions and namespaces + +### 3.1 clojure.math and numeric helper functions + +Added a new clojure.math namespace which provides wrappers for the functions available in java.lang.Math. +These functions are narrowed to only `long` and `double` overloads and provide primitive support without reflection. + +In addition, the following functions were added to clojure.core: + +* `abs` - absolute value in optimized form for all Clojure numeric types (long, double, ratio, bigint, bigdecimal) +* `NaN?` - predicate for doubles to check whether "not a number" +* `infinite?` - predicate for doubles to check whether positive or negative infinity + +* [CLJ-2668](https://clojure.atlassian.net/browse/CLJ-2668) Add NaN? and infinite? predicates +* [CLJ-2664](https://clojure.atlassian.net/browse/CLJ-2664) Add clojure.java.math namespace, wrappers for java.lang.Math +* [CLJ-2673](https://clojure.atlassian.net/browse/CLJ-2673) Add `abs`, and update `min` and `max` to use Math impls when possible +* [CLJ-2677](https://clojure.atlassian.net/browse/CLJ-2677) clojure.math - fix method reflection in bodies and inlines, fix docstrings, renamed +* [CLJ-2689](https://clojure.atlassian.net/browse/CLJ-2689) Fix clojure.math tests to be more tolerant of floating point comparisons + +### 3.2 Parser functions + +Added the following parsing functions to clojure.core: + +* `parse-double` - parses floating point number, including scientific notation +* `parse-long` - parses integer in long range +* `parse-boolean` - parses `"true"` or `"false"` to the canonical boolean values +* `parse-uuid` - parses a UUID string to java.util.UUID + +All of these functions expect a string argument and return either the parsed value or `nil` if the value +is in invalid format. + +* [CLJ-2667](https://clojure.atlassian.net/browse/CLJ-2667) Add functions to parse a single long/double/uuid/boolean from a string + +### 3.2 `random-uuid` + +Added `random-uuid`, a function to construct a random java.util.UUID. + +* [CLJ-1925](https://clojure.atlassian.net/browse/CLJ-1925) Add random-uuid + +### 3.3 `update-keys` and `update-vals` + +Added: + +* `update-keys` - applies a function to every key in a map, `m f => {(f k) v ...}` +* `update-vals` - applies a function to every value in a map, `m f => {k (f v) ...}` + +* [CLJ-1959](https://clojure.atlassian.net/browse/CLJ-1959) Add implementation of update-keys +* [CLJ-2651](https://clojure.atlassian.net/browse/CLJ-2651) Add implementation of update-vals + +### 3.4 `iteration` + +Added `iteration`, to repeatedly apply a (possibly impure) step function with continuation state. +This can be used e.g. to consume APIs that return paginated or batched data. + +* [CLJ-2555](https://clojure.atlassian.net/browse/CLJ-2555) Add `iteration` generator function +* [CLJ-2690](https://clojure.atlassian.net/browse/CLJ-2690) Improve `iteration` docstring and arg names +* [CLJ-2685](https://clojure.atlassian.net/browse/CLJ-2685) Fix `iteration` generative test failure + +## 4 Fixes + +### 4.1 Compiler + +* [CLJ-2680](https://clojure.atlassian.net/browse/CLJ-2680) Fix type hinting a primitive local with matching type hint to not error +* [CLJ-1180](https://clojure.atlassian.net/browse/CLJ-1180) Fix resolution of class type hints in `defprotocol` +* [CLJ-1973](https://clojure.atlassian.net/browse/CLJ-1973) Make order of emitted protocol methods in generated classes reproducible + +### 4.2 Core + +* [CLJ-1879](https://clojure.atlassian.net/browse/CLJ-1879) IKVReduce - make IPersistentMap case faster and extend to Object, detaching it from any fully enumerable set of types +* [CLJ-2065](https://clojure.atlassian.net/browse/CLJ-2065) IKVReduce - add direct support for SubVector +* [CLJ-2663](https://clojure.atlassian.net/browse/CLJ-2663) Fix vector `=` not terminating when called with infinite sequence +* [CLJ-2679](https://clojure.atlassian.net/browse/CLJ-2679) Fix hash collisions in `case` expressions on symbols +* [CLJ-2600](https://clojure.atlassian.net/browse/CLJ-2600) Don't block `realized?` of `delay` on pending result +* [CLJ-2649](https://clojure.atlassian.net/browse/CLJ-2649) Fix order of checks in `some-fn` and `every-pred` for 3 predicate case to match other unrollings +* [CLJ-2234](https://clojure.atlassian.net/browse/CLJ-2234) Fix multimethod preferences to correctly use local hierarchy when it exists +* [CLJ-2556](https://clojure.atlassian.net/browse/CLJ-2556) Fix `into` completion so `halt-when` works + +### 4.3 Performance + +* [CLJ-1808](https://clojure.atlassian.net/browse/CLJ-1808) `map-invert` should use `reduce-kv` and transient +* [CLJ-2621](https://clojure.atlassian.net/browse/CLJ-2621) Fix unnecessary boxing of unused return in statement context for instance method expr +* [CLJ-2670](https://clojure.atlassian.net/browse/CLJ-2670) Use Math.exact... methods for checked long math ops for performance +* [CLJ-2636](https://clojure.atlassian.net/browse/CLJ-2636) Get rid of reflection on java.util.Properties when defining `*clojure-version*` +* [CLJ-1509](https://clojure.atlassian.net/browse/CLJ-1509) AOT compile clojure.instant, clojure.uuid, clojure.core.reducers in build + +### 4.4 Error messages + +* [CLJ-2529](https://clojure.atlassian.net/browse/CLJ-2529) Fix incorrect reporting of runtime errors as compiler errors in calls through `Compiler.load()` +* [CLJ-2350](https://clojure.atlassian.net/browse/CLJ-2350) Improve keyword arity exception message + +### 4.5 Docstrings + +* [CLJ-2249](https://clojure.atlassian.net/browse/CLJ-2249) Clarify `get` docstring regarding sets, strings, arrays, ILookup +* [CLJ-2488](https://clojure.atlassian.net/browse/CLJ-2488) Add definition to `reify` docstring +* [CLJ-1360](https://clojure.atlassian.net/browse/CLJ-1360) Update `clojure.string/split` docstring regarding trailing empty parts +* [CLJ-2444](https://clojure.atlassian.net/browse/CLJ-2444) Fix typo in `test-vars` docstring +* [CLJ-2666](https://clojure.atlassian.net/browse/CLJ-2666) Make Clojure Java API javadoc text match the example + +### 4.6 Other enhancements + +* [CLJ-2493](https://clojure.atlassian.net/browse/CLJ-2493) clojure.java.browse - Fix `browse-url` hanging on call to xdg-open +* [CLJ-1908](https://clojure.atlassian.net/browse/CLJ-1908) clojure.test - Add `run-test` and `run-test-var` to run single test with fixtures and report +* [CLJ-1379](https://clojure.atlassian.net/browse/CLJ-1379) clojure.test - Fix quoting of `:actual` form in `:pass` maps +* [CLJ-2620](https://clojure.atlassian.net/browse/CLJ-2620) clojure.server - Fix asymmetric handling of `:exception` `:val`s in `prepl` +* [CLJ-2387](https://clojure.atlassian.net/browse/CLJ-2387) clojure.server - Fix off-by-one in socket server port validation + + # Changes to Clojure in Version 1.10.3 ## 1 Changes reverted diff --git a/clojure.iml b/clojure.iml deleted file mode 100644 index c1c8a6a84a..0000000000 --- a/clojure.iml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/codegen/gen_math.clj b/codegen/gen_math.clj new file mode 100644 index 0000000000..484649d3d3 --- /dev/null +++ b/codegen/gen_math.clj @@ -0,0 +1,263 @@ +;; This code was used to generate the clojure.math namespace in +;; Clojure 1.11 to wrap Java 1.8 java.lang.Math methods. There are +;; many small tweaks in this to get exactly the output that was +;; desired and it was not intended to be reused in any way, it is +;; included here for future reference. + +(ns gen-math + (:require + [clojure.reflect :as reflect] + [clojure.set :as set] + [clojure.string :as str]) + (:import + [java.io StringWriter Writer])) + +;; manually created +(declare HEADER) +(declare FNS) +(declare DOCS) +(declare ARGS) +(declare ARGTYPES) + +(def const-template + "(def + ^{:doc %s + :added %s + :const true + :tag %s} + %s + %s)\n\n") + +(defn- emit-constant + [^Writer writer {:keys [cname name added type]}] + (let [sym (symbol (str cname) (str name)) + doc (str "\"" (get DOCS (symbol name)) "\"") + tag (str "'" type)] + (.write writer + (format const-template doc (pr-str added) tag name sym)))) + +(def fn-template + "(defn %s + {:doc %s + :inline-arities %s + :inline %s + :added %s} + %s%s + %s)\n\n") + +(defn- clojurize + [sym] + (or + (get '{IEEEremainder IEEE-remainder} sym) + (let [s (name sym)] + (symbol + (str + (reduce + (fn [^StringBuilder b ^Character c] + (if (Character/isUpperCase c) + (.. b (append "-") (append (Character/toLowerCase c))) + (.append b c))) + (StringBuilder.) + s)))))) + +(defn- inline-body + [params param-types] + (str/join " " + (map (fn [p pt] (format "(%s ~%s)" pt p)) + params param-types))) + +(defn- body + [params param-types on-types] + (map (fn [p pt] (if (contains? on-types pt) `(~pt ~p) p)) + params param-types)) + +(defn- emit-fn + [^Writer writer {:keys [cname fname sigs]}] + (let [sym (symbol (str cname) (str fname)) + arities (group-by #(-> % :parameter-types count) sigs) + arity (-> arities keys first) ;; NOTE: ignore multiple arities, none in Math + arity-sigs (get arities arity) + cname (clojurize fname) + doc (str "\"" (get DOCS cname) "\"") + sig (if (= 1 (count arity-sigs)) (first arity-sigs) (get ARGTYPES cname)) + {pts :parameter-types, rt :return-type} sig + ps (get ARGS cname) + ;; coerce all args in inline body + inline-body (format "(fn %s `(%s%s))" (pr-str ps) (if (< 0 (count ps)) (str sym " ") sym) (inline-body ps pts)) + ;; ps are hinted, so coerce only ps that can't be hinted - int type + body `(~sym ~@(body ps pts #{'int})) + rts (if (#{'long 'double} rt) (str "^" rt " ") "") + hints (map #(if (#{'long 'double} %) (symbol (str "^" %)) nil) pts) + pst (vec (remove nil? (interleave hints ps)))] + (.write writer + (format fn-template cname doc #{arity} inline-body (pr-str "1.11") rts pst body)))) + +(defn gen-static-wrappers + [csym] + (let [added "1.11" + members (:members (reflect/type-reflect (resolve csym))) + statics (filter #(set/subset? #{:public :static} (:flags %)) members) + {fs false, ms true} (group-by #(contains? % :return-type) statics) + methods (->> ms (filter (fn [m] + (or (= 'scalb (:name m)) + (empty? (set/intersection #{'int 'float} (set (:parameter-types m)))))))) + by-name (group-by :name methods) + writer (StringWriter.)] + (.write writer HEADER) + (doseq [f fs] + (emit-constant writer (merge f {:cname csym, :added added}))) + (doseq [n FNS] + (emit-fn writer {:cname csym, :fname n, :added added, :sigs (get by-name n)})) + (spit "src/clj/clojure/math.clj" (str writer)))) + +(comment + (gen-static-wrappers 'Math) + ) + +;;;; Manually provided info used during the generator + +(def ^String HEADER + "; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns + ^{:author \"Alex Miller\", + :doc \"Clojure wrapper functions for java.lang.Math static methods. + + Function calls are inlined for performance, and type hinted for primitive + long or double parameters where appropriate. In general, Math methods are + optimized for performance and have bounds for error tolerance. If + greater precision is needed, use java.lang.StrictMath directly instead. + + For more complete information, see: + https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html\"} + clojure.math) + +(set! *warn-on-reflection* true) + +") + +;; fns + +;; omitted: toIntExact +;; omitted but include in core w/polymorphic impl: abs, min, max +(def FNS + '[sin cos tan asin acos atan toRadians toDegrees exp log log10 + sqrt cbrt IEEEremainder ceil floor rint atan2 pow round random + addExact subtractExact multiplyExact incrementExact decrementExact negateExact + floorDiv floorMod ulp signum sinh cosh tanh hypot expm1 log1p copySign getExponent + nextAfter nextUp nextDown scalb]) + +;; docstrings to use +(def DOCS + '{ + E "Constant for e, the base for natural logarithms.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#E" + PI "Constant for pi, the ratio of the circumference of a circle to its diameter.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#PI" + sin "Returns the sine of an angle.\n If a is ##NaN, ##-Inf, ##Inf => ##NaN\n If a is zero => zero with the same sign as a\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#sin-double-" + cos "Returns the cosine of an angle.\n If a is ##NaN, ##-Inf, ##Inf => ##NaN\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#cos-double-" + tan "Returns the tangent of an angle.\n If a is ##NaN, ##-Inf, ##Inf => ##NaN\n If a is zero => zero with the same sign as a\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#tan-double-" + asin "Returns the arc sine of an angle, in the range -pi/2 to pi/2.\n If a is ##NaN or |a|>1 => ##NaN\n If a is zero => zero with the same sign as a\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#asin-double-" + acos "Returns the arc cosine of a, in the range 0.0 to pi.\n If a is ##NaN or |a|>1 => ##NaN\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#acos-double-" + atan "Returns the arc tangent of a, in the range of -pi/2 to pi/2.\n If a is ##NaN => ##NaN\n If a is zero => zero with the same sign as a\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#atan-double-" + to-radians "Converts an angle in degrees to an approximate equivalent angle in radians.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#toRadians-double-" + to-degrees "Converts an angle in radians to an approximate equivalent angle in degrees.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#toDegrees-double-" + exp "Returns Euler's number e raised to the power of a.\n If a is ##NaN => ##NaN\n If a is ##Inf => ##Inf\n If a is ##-Inf => +0.0\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#exp-double-" + log "Returns the natural logarithm (base e) of a.\n If a is ##NaN or negative => ##NaN\n If a is ##Inf => ##Inf\n If a is zero => ##-Inf\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#log-double-" + log10 "Returns the logarithm (base 10) of a.\n If a is ##NaN or negative => ##NaN\n If a is ##Inf => ##Inf\n If a is zero => ##-Inf\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#log10-double-" + sqrt "Returns the positive square root of a.\n If a is ##NaN or negative => ##NaN\n If a is ##Inf => ##Inf\n If a is zero => a\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#sqrt-double-" + cbrt "Returns the cube root of a.\n If a is ##NaN => ##NaN\n If a is ##Inf or ##-Inf => a\n If a is zero => zero with sign matching a\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#cbrt-double-" + IEEE-remainder "Returns the remainder per IEEE 754 such that\n remainder = dividend - divisor * n\n where n is the integer closest to the exact value of dividend / divisor.\n If two integers are equally close, then n is the even one.\n If the remainder is zero, sign will match dividend.\n If dividend or divisor is ##NaN, or dividend is ##Inf or ##-Inf, or divisor is zero => ##NaN\n If dividend is finite and divisor is infinite => dividend\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#IEEEremainder-double-double-" + ceil "Returns the smallest double greater than or equal to a, and equal to a\n mathematical integer.\n If a is ##NaN or ##Inf or ##-Inf or already equal to an integer => a\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#ceil-double-" + floor "Returns the largest double less than or equal to a, and equal to a\n mathematical integer.\n If a is ##NaN or ##Inf or ##-Inf or already equal to an integer => a\n If a is less than zero but greater than -1.0 => -0.0\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#floor-double-" + rint "Returns the double closest to a and equal to a mathematical integer.\n If two values are equally close, return the even one.\n If a is ##NaN or ##Inf or ##-Inf or zero => a\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#rint-double-" + atan2 "Returns the angle theta from the conversion of rectangular coordinates (x, y) to polar coordinates (r, theta).\n Computes the phase theta by computing an arc tangent of y/x in the range of -pi to pi.\n For more details on special cases, see:\n https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#atan2-double-double-" + pow "Returns the value of a raised to the power of b.\n For more details on special cases, see:\n https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#pow-double-double-" + round "Returns the closest long to a. If equally close to two values, return the one\n closer to ##Inf.\n If a is ##NaN => 0\n If a is ##-Inf or < Long/MIN_VALUE => Long/MIN_VALUE\n If a is ##Inf or > Long/MAX_VALUE => Long/MAX_VALUE\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#round-double-" + random "Returns a positive double between 0.0 and 1.0, chosen pseudorandomly with\n approximately random distribution.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#random--" + add-exact "Returns the sum of x and y, throws ArithmeticException on overflow.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#addExact-long-long-" + subtract-exact "Returns the difference of x and y, throws ArithmeticException on overflow.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#subtractExact-long-long-" + multiply-exact "Returns the product of x and y, throws ArithmeticException on overflow.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#multiplyExact-long-long-" + increment-exact "Returns a incremented by 1, throws ArithmeticException on overflow.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#incrementExact-long-" + decrement-exact "Returns a decremented by 1, throws ArithmeticException on overflow.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#decrementExact-long-" + negate-exact "Returns the negation of a, throws ArithmeticException on overflow.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#negateExact-long-" + floor-div "Integer division that rounds to negative infinity (as opposed to zero).\n The special case (floorDiv Long/MIN_VALUE -1) overflows and returns Long/MIN_VALUE.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#floorDiv-long-long-" + floor-mod "Integer modulus x - (floorDiv(x, y) * y). Sign matches y and is in the\n range -|y| < r < |y|.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#floorMod-long-long-" + ulp "Returns the size of an ulp (unit in last place) for d.\n If d is ##NaN => ##NaN\n If d is ##Inf or ##-Inf => ##Inf\n If d is zero => Double/MIN_VALUE\n If d is +/- Double/MAX_VALUE => 2^971\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#ulp-double-" + signum "Returns the signum function of d - zero for zero, 1.0 if >0, -1.0 if <0.\n If d is ##NaN => ##NaN\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#signum-double-" + sinh "Returns the hyperbolic sine of x, (e^x - e^-x)/2.\n If x is ##NaN => ##NaN\n If x is ##Inf or ##-Inf or zero => x\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#sinh-double-" + cosh "Returns the hyperbolic cosine of x, (e^x + e^-x)/2.\n If x is ##NaN => ##NaN\n If x is ##Inf or ##-Inf => ##Inf\n If x is zero => 1.0\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#cosh-double-" + tanh "Returns the hyperbolic tangent of x, sinh(x)/cosh(x).\n If x is ##NaN => ##NaN\n If x is zero => zero, with same sign\n If x is ##Inf => +1.0\n If x is ##-Inf => -1.0\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#tanh-double-" + hypot "Returns sqrt(x^2 + y^2) without intermediate underflow or overflow.\n If x or y is ##Inf or ##-Inf => ##Inf\n If x or y is ##NaN and neither is ##Inf or ##-Inf => ##NaN\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#hypot-double-double-" + expm1 "Returns e^x - 1. Near 0, expm1(x)+1 is more accurate to e^x than exp(x).\n If x is ##NaN => ##NaN\n If x is ##Inf => #Inf\n If x is ##-Inf => -1.0\n If x is zero => x\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#expm1-double-" + log1p "Returns ln(1+x). For small values of x, log1p(x) is more accurate than\n log(1.0+x).\n If x is ##NaN or < -1 => ##NaN\n If x is ##Inf => ##Inf\n If x is -1 => ##-Inf\n If x is 0 => 0 with sign matching x\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#log1p-double-" + copy-sign "Returns a double with the magnitude of the first argument and the sign of\n the second.\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#copySign-double-double-" + get-exponent "Returns the exponent of d.\n If d is ##NaN, ##Inf, ##-Inf => Double/MAX_EXPONENT + 1\n If d is zero or subnormal => Double/MIN_EXPONENT - 1\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#getExponent-double-" + next-after "Returns the adjacent floating point number to start in the direction of\n the second argument. If the arguments are equal, the second is returned.\n If either arg is #NaN => #NaN\n If both arguments are signed zeros => direction\n If start is +-Double/MIN_VALUE and direction would cause a smaller magnitude\n => zero with sign matching start\n If start is ##Inf or ##-Inf and direction would cause a smaller magnitude\n => Double/MAX_VALUE with same sign as start\n If start is equal to +=Double/MAX_VALUE and direction would cause a larger magnitude\n => ##Inf or ##-Inf with sign matching start\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextAfter-double-double-" + next-up "Returns the adjacent double of d in the direction of ##Inf.\n If d is ##NaN => ##NaN\n If d is ##Inf => ##Inf\n If d is zero => Double/MIN_VALUE\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextUp-double-" + next-down "Returns the adjacent double of d in the direction of ##-Inf.\n If d is ##NaN => ##NaN\n If d is ##-Inf => ##-Inf\n If d is zero => -Double/MIN_VALUE\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextDown-double-" + scalb "Returns d * 2^scaleFactor, scaling by a factor of 2. If the exponent\n is between Double/MIN_EXPONENT and Double/MAX_EXPONENT, the answer is exact.\n If d is ##NaN => ##NaN\n If d is ##Inf or ##-Inf => ##Inf or ##-Inf respectively\n If d is zero => zero of same sign as d\n See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextDown-double-" + }) + +(def FNS + '[sin cos tan asin acos atan toRadians toDegrees exp log log10 + sqrt cbrt IEEEremainder ceil floor rint atan2 pow round random + addExact subtractExact multiplyExact incrementExact decrementExact negateExact + floorDiv floorMod ulp signum sinh cosh tanh hypot expm1 log1p copySign getExponent + nextAfter nextUp nextDown scalb]) + +;; arg names to use (match java.lang.Math signatures) +(def ARGS + '{ + sin [a] + cos [a] + tan [a] + asin [a] + acos [a] + atan [a] + to-radians [deg] + to-degrees [r] + exp [a] + log [a] + log10 [a] + sqrt [a] + cbrt [a] + IEEE-remainder [dividend divisor] + ceil [a] + floor [a] + rint [a] + atan2 [y x] + pow [a b] + round [a] + random [] + add-exact [x y] + subtract-exact [x y] + multiply-exact [x y] + increment-exact [a] + decrement-exact [a] + negate-exact [a] + floor-div [x y] + floor-mod [x y] + ulp [d] + signum [d] + sinh [x] + cosh [x] + tanh [x] + hypot [x y] + expm1 [x] + log1p [x] + copy-sign [magnitude sign] + get-exponent [d] + next-after [start direction] + next-up [d] + next-down [d] + scalb [d scaleFactor] + }) + +;; type signature to use (otherwise automatically determined) +(def ARGTYPES + '{scalb {:parameter-types [double int] :return-type double}}) diff --git a/pom.xml b/pom.xml index 300eaff009..a36970cfce 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ clojure clojure jar - 1.10.3 + 1.11.2 http://clojure.org/ Clojure core environment and runtime library. @@ -30,7 +30,7 @@ scm:git:git@github.com:clojure/clojure.git scm:git:git@github.com:clojure/clojure.git git@github.com:clojure/clojure.git - clojure-1.10.3 + clojure-1.11.2 @@ -41,17 +41,17 @@ org.clojure spec.alpha - 0.2.194 + 0.3.218 org.clojure core.specs.alpha - 0.2.56 + 0.2.62 org.clojure test.generative - 0.5.2 + 1.0.0 test @@ -63,7 +63,7 @@ org.clojure test.check - 0.9.0 + 1.1.1 test @@ -207,7 +207,7 @@ instead, push SCM changes in Hudson configuration --> org.apache.maven.plugins maven-release-plugin - 2.4.1 + 2.5.3 false true @@ -227,7 +227,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.7 + 1.6.8 true diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj index 2c7334febc..1e7c6977cc 100644 --- a/src/clj/clojure/core.clj +++ b/src/clj/clojure/core.clj @@ -1134,6 +1134,18 @@ ([x y & more] (reduce1 min (min x y) more))) +(defn abs + {:doc "Returns the absolute value of a. + If a is Long/MIN_VALUE => Long/MIN_VALUE + If a is a double and zero => +0.0 + If a is a double and ##Inf or ##-Inf => ##Inf + If a is a double and ##NaN => ##NaN" + :inline-arities #{1} + :inline (fn [a] `(clojure.lang.Numbers/abs ~a)) + :added "1.11"} + [a] + (clojure.lang.Numbers/abs a)) + (defn dec' "Returns a number one less than num. Supports arbitrary precision. See also: dec" @@ -1494,7 +1506,8 @@ [coll key] (. clojure.lang.RT (contains coll key))) (defn get - "Returns the value mapped to key, not-found or nil if key not present." + "Returns the value mapped to key, not-found or nil if key not present + in associative collection, set, string, array, or ILookup instance." {:inline (fn [m k & nf] `(. clojure.lang.RT (get ~m ~k ~@nf))) :inline-arities #{2 3} :added "1.0"} @@ -3015,7 +3028,8 @@ [n x] (take n (repeat x))) (defn iterate - "Returns a lazy sequence of x, (f x), (f (f x)) etc. f must be free of side-effects" + "Returns a lazy (infinite!) sequence of x, (f x), (f (f x)) etc. + f must be free of side-effects" {:added "1.0" :static true} [f x] (clojure.lang.Iterate/create f x) ) @@ -4371,7 +4385,19 @@ :static true} ([] (. clojure.lang.PersistentArrayMap EMPTY)) ([& keyvals] - (clojure.lang.PersistentArrayMap/createAsIfByAssoc (to-array keyvals)))) + (let [ary (to-array keyvals)] + (if (odd? (alength ary)) + (throw (IllegalArgumentException. (str "No value supplied for key: " (last keyvals)))) + (clojure.lang.PersistentArrayMap/createAsIfByAssoc ary))))) + +(defn seq-to-map-for-destructuring + "Builds a map from a seq as described in + https://clojure.org/reference/special_forms#keyword-arguments" + {:added "1.11"} + [s] + (if (next s) + (clojure.lang.PersistentArrayMap/createAsIfByAssoc (to-array s)) + (if (seq s) (first s) clojure.lang.PersistentArrayMap/EMPTY))) ;;redefine let and loop with destructuring (defn destructure [bindings] @@ -4419,7 +4445,11 @@ 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)) + (conj gmap) (conj `(if (seq? ~gmap) + (if (next ~gmapseq) + (clojure.lang.PersistentArrayMap/createAsIfByAssoc (to-array ~gmapseq)) + (if (seq ~gmapseq) (first ~gmapseq) clojure.lang.PersistentArrayMap/EMPTY)) + ~gmap)) ((fn [ret] (if (:as b) (conj ret (:as b) gmap) @@ -4468,10 +4498,15 @@ (defmacro let "binding => binding-form init-expr + binding-form => name, or destructuring-form + destructuring-form => map-destructure-form, or seq-destructure-form Evaluates the exprs in a lexical context in which the symbols in the binding-forms are bound to their respective init-exprs or parts - therein." + therein. + + See https://clojure.org/reference/special_forms#binding-forms for + more information about destructuring." {:added "1.0", :special-form true, :forms '[(let [bindings*] exprs*)]} [bindings & body] (assert-args @@ -4499,12 +4534,14 @@ ;redefine fn with destructuring and pre/post conditions (defmacro fn - "params => positional-params* , or positional-params* & next-param + "params => positional-params*, or positional-params* & rest-param positional-param => binding-form - next-param => binding-form - name => symbol + rest-param => binding-form + binding-form => name, or destructuring-form - Defines a function" + Defines a function. + + See https://clojure.org/reference/special_forms#fn for more information" {:added "1.0", :special-form true, :forms '[(fn name? [params* ] exprs*) (fn name? ([params* ] exprs*)+)]} [& sigs] @@ -5922,13 +5959,15 @@ (name lib) prefix) (let [lib (if prefix (symbol (str prefix \. lib)) lib) opts (apply hash-map options) - {:keys [as reload reload-all require use verbose]} opts + {:keys [as reload reload-all require use verbose as-alias]} opts loaded (contains? @*loaded-libs* lib) - load (cond reload-all - load-all - (or reload (not require) (not loaded)) - load-one) need-ns (or as use) + load (cond reload-all load-all + reload load-one + (not loaded) (cond need-ns load-one + as-alias (fn [lib _need _require] (create-ns lib)) + :else load-one)) + filter-opts (select-keys opts '(:exclude :only :rename :refer)) undefined-on-entry (not (find-ns lib))] (binding [*loading-verbosely* (or *loading-verbosely* verbose)] @@ -5940,13 +5979,17 @@ (remove-ns lib)) (throw e))) (throw-if (and need-ns (not (find-ns lib))) - "namespace '%s' not found" lib)) + "namespace '%s' not found" lib)) (when (and need-ns *loading-verbosely*) (printf "(clojure.core/in-ns '%s)\n" (ns-name *ns*))) (when as (when *loading-verbosely* (printf "(clojure.core/alias '%s '%s)\n" as lib)) (alias as lib)) + (when as-alias + (when *loading-verbosely* + (printf "(clojure.core/alias '%s '%s)\n" as-alias lib)) + (alias as-alias lib)) (when (or use (:refer filter-opts)) (when *loading-verbosely* (printf "(clojure.core/refer '%s" lib) @@ -5963,7 +6006,7 @@ opts (interleave flags (repeat true)) args (filter (complement keyword?) args)] ; check for unsupported options - (let [supported #{:as :reload :reload-all :require :use :verbose :refer} + (let [supported #{:as :reload :reload-all :require :use :verbose :refer :as-alias} unsupported (seq (remove supported flags))] (throw-if unsupported (apply str "Unsupported option(s) supplied: " @@ -6027,6 +6070,9 @@ Recognized options: :as takes a symbol as its argument and makes that symbol an alias to the lib's namespace in the current namespace. + :as-alias takes a symbol as its argument and aliases like :as, however + the lib will not be loaded. If the lib has not been loaded, a new + empty namespace will be created (as with create-ns). :refer takes a list of symbols to refer from the namespace or the :all keyword to bring in all public vars. @@ -6043,9 +6089,10 @@ A flag is a keyword. Recognized flags: :reload, :reload-all, :verbose :reload forces loading of all the identified libs even if they are - already loaded + already loaded (has no effect on libspecs using :as-alias) :reload-all implies :reload and also forces loading of all libs that the identified libs directly or indirectly load via require or use + (has no effect on libspecs using :as-alias) :verbose triggers printing information about each load, alias, and refer Example: @@ -6438,7 +6485,10 @@ fails, attempts to require sym's namespace and retries." Supported options: :elide-meta - a collection of metadata keys to elide during compilation. :disable-locals-clearing - set to true to disable clearing, useful for using a debugger - Alpha, subject to change." + :direct-linking - set to true to use direct static invocation of functions, rather than vars + Note that call sites compiled with direct linking will not be affected by var redefinition. + Use ^:redef (or ^:dynamic) on a var to prevent direct linking and allow redefinition. + See https://clojure.org/reference/compilation for more information." {:added "1.4"}) (add-doc-and-meta *ns* @@ -6652,7 +6702,7 @@ fails, attempts to require sym's namespace and retries." (next ks) (next vs)) m)) assoc-multi (fn [m h bucket] - (let [testexprs (apply concat bucket) + (let [testexprs (mapcat (fn [kv] [(list 'quote (first kv)) (second kv)]) bucket) expr `(condp = ~expr-sym ~@testexprs ~default)] (assoc m h expr))) hmap (reduce1 @@ -6809,6 +6859,13 @@ fails, attempts to require sym's namespace and retries." {:added "1.9"} [x] (instance? java.util.UUID x)) +(defn random-uuid + {:doc "Returns a pseudo-randomly generated java.util.UUID instance (i.e. type 4). + + See: https://docs.oracle.com/javase/8/docs/api/java/util/UUID.html#randomUUID--" + :added "1.11"} + ^java.util.UUID [] (java.util.UUID/randomUUID)) + (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 @@ -6836,13 +6893,18 @@ fails, attempts to require sym's namespace and retries." init) ;;slow path default - clojure.lang.IPersistentMap - (kv-reduce + java.lang.Object + (kv-reduce [amap f init] - (reduce (fn [ret [k v]] (f ret k v)) init amap)) + (reduce (fn [ret ^java.util.Map$Entry me] + (f ret + (.getKey me) + (.getValue me))) + init + amap)) clojure.lang.IKVReduce - (kv-reduce + (kv-reduce [amap f init] (.kvreduce amap f init))) @@ -6899,7 +6961,11 @@ fails, attempts to require sym's namespace and retries." (reduce conj to from))) ([to xform from] (if (instance? clojure.lang.IEditableCollection to) - (with-meta (persistent! (transduce xform conj! (transient to) from)) (meta to)) + (let [tm (meta to) + rf (fn + ([coll] (-> (persistent! coll) (with-meta tm))) + ([coll v] (conj! coll v)))] + (transduce xform rf (transient to) from)) (transduce xform conj to from)))) (defn mapv @@ -7054,7 +7120,8 @@ fails, attempts to require sym's namespace and retries." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; clojure version number ;;;;;;;;;;;;;;;;;;;;;; -(let [properties (with-open [version-stream (.getResourceAsStream +(let [^java.util.Properties + properties (with-open [version-stream (.getResourceAsStream (clojure.lang.RT/baseLoader) "clojure/version.properties")] (doto (new java.util.Properties) @@ -7421,8 +7488,8 @@ fails, attempts to require sym's namespace and retries." (fn ep3 ([] true) ([x] (boolean (and (p1 x) (p2 x) (p3 x)))) - ([x y] (boolean (and (p1 x) (p2 x) (p3 x) (p1 y) (p2 y) (p3 y)))) - ([x y z] (boolean (and (p1 x) (p2 x) (p3 x) (p1 y) (p2 y) (p3 y) (p1 z) (p2 z) (p3 z)))) + ([x y] (boolean (and (p1 x) (p1 y) (p2 x) (p2 y) (p3 x) (p3 y)))) + ([x y z] (boolean (and (p1 x) (p1 y) (p1 z) (p2 x) (p2 y) (p2 z) (p3 x) (p3 y) (p3 z)))) ([x y z & args] (boolean (and (ep3 x y z) (every? #(and (p1 %) (p2 %) (p3 %)) args)))))) ([p1 p2 p3 & ps] @@ -7461,8 +7528,8 @@ fails, attempts to require sym's namespace and retries." (fn sp3 ([] nil) ([x] (or (p1 x) (p2 x) (p3 x))) - ([x y] (or (p1 x) (p2 x) (p3 x) (p1 y) (p2 y) (p3 y))) - ([x y z] (or (p1 x) (p2 x) (p3 x) (p1 y) (p2 y) (p3 y) (p1 z) (p2 z) (p3 z))) + ([x y] (or (p1 x) (p1 y) (p2 x) (p2 y) (p3 x) (p3 y))) + ([x y z] (or (p1 x) (p1 y) (p1 z) (p2 x) (p2 y) (p2 z) (p3 x) (p3 y) (p3 z))) ([x y z & args] (or (sp3 x y z) (some #(or (p1 %) (p2 %) (p3 %)) args))))) ([p1 p2 p3 & ps] @@ -7717,6 +7784,52 @@ fails, attempts to require sym's namespace and retries." (reduce #(proc %2) nil coll) nil) +(defn iteration + "Creates a seqable/reducible via repeated calls to step, + a function of some (continuation token) 'k'. The first call to step + will be passed initk, returning 'ret'. Iff (somef ret) is true, + (vf ret) will be included in the iteration, else iteration will + terminate and vf/kf will not be called. If (kf ret) is non-nil it + will be passed to the next step call, else iteration will terminate. + + This can be used e.g. to consume APIs that return paginated or batched data. + + step - (possibly impure) fn of 'k' -> 'ret' + + :somef - fn of 'ret' -> logical true/false, default 'some?' + :vf - fn of 'ret' -> 'v', a value produced by the iteration, default 'identity' + :kf - fn of 'ret' -> 'next-k' or nil (signaling 'do not continue'), default 'identity' + :initk - the first value passed to step, default 'nil' + + It is presumed that step with non-initk is unreproducible/non-idempotent. + If step with initk is unreproducible it is on the consumer to not consume twice." + {:added "1.11"} + [step & {:keys [somef vf kf initk] + :or {vf identity + kf identity + somef some? + initk nil}}] + (reify + clojure.lang.Seqable + (seq [_] + ((fn next [ret] + (when (somef ret) + (cons (vf ret) + (when-some [k (kf ret)] + (lazy-seq (next (step k))))))) + (step initk))) + clojure.lang.IReduceInit + (reduce [_ rf init] + (loop [acc init + ret (step initk)] + (if (somef ret) + (let [acc (rf acc (vf ret))] + (if (reduced? acc) + @acc + (if-some [k (kf ret)] + (recur acc (step k)) + acc))) + acc))))) (defn tagged-literal? "Return true if the value is the data representation of a tagged literal" @@ -7892,3 +8005,102 @@ fails, attempts to require sym's namespace and retries." [x] (force tap-loop) (.offer tapq (if (nil? x) ::tap-nil x))) + +(defn update-vals + "m f => {k (f v) ...} + + Given a map m and a function f of 1-argument, returns a new map where the keys of m + are mapped to result of applying f to the corresponding values of m." + {:added "1.11"} + [m f] + (with-meta + (persistent! + (reduce-kv (fn [acc k v] (assoc! acc k (f v))) + (if (instance? clojure.lang.IEditableCollection m) + (transient m) + (transient {})) + m)) + (meta m))) + +(defn update-keys + "m f => {(f k) v ...} + + Given a map m and a function f of 1-argument, returns a new map whose + keys are the result of applying f to the keys of m, mapped to the + corresponding values of m. + f must return a unique key for each key of m, else the behavior is undefined." + {:added "1.11"} + [m f] + (let [ret (persistent! + (reduce-kv (fn [acc k v] (assoc! acc (f k) v)) + (transient {}) + m))] + (with-meta ret (meta m)))) + +(defn- parsing-err + "Construct message for parsing for non-string parsing error" + ^String [val] + (str "Expected string, got " (if (nil? val) "nil" (-> val class .getName)))) + +(defn parse-long + {:doc "Parse string of decimal digits with optional leading -/+ and return a + Long value, or nil if parse fails" + :added "1.11"} + ^Long [^String s] + (if (string? s) + (try + (Long/valueOf s) + (catch NumberFormatException _ nil)) + (throw (IllegalArgumentException. (parsing-err s))))) + +(defn parse-double + {:doc "Parse string with floating point components and return a Double value, + or nil if parse fails. + + Grammar: https://docs.oracle.com/javase/8/docs/api/java/lang/Double.html#valueOf-java.lang.String-" + :added "1.11"} + ^Double [^String s] + (if (string? s) + (try + (Double/valueOf s) + (catch NumberFormatException _ nil)) + (throw (IllegalArgumentException. (parsing-err s))))) + +(defn parse-uuid + {:doc "Parse a string representing a UUID and return a java.util.UUID instance, + or nil if parse fails. + + Grammar: https://docs.oracle.com/javase/8/docs/api/java/util/UUID.html#toString--" + :added "1.11"} + ^java.util.UUID [^String s] + (try + (java.util.UUID/fromString s) + (catch IllegalArgumentException _ nil))) + +(defn parse-boolean + {:doc "Parse strings \"true\" or \"false\" and return a boolean, or nil if invalid" + :added "1.11"} + [^String s] + (if (string? s) + (case s + "true" true + "false" false + nil) + (throw (IllegalArgumentException. (parsing-err s))))) + +(defn NaN? + {:doc "Returns true if num is NaN, else false" + :inline-arities #{1} + :inline (fn [num] `(Double/isNaN ~num)) + :added "1.11"} + + [^double num] + (Double/isNaN num)) + +(defn infinite? + {:doc "Returns true if num is negative or positive infinity, else false" + :inline-arities #{1} + :inline (fn [num] `(Double/isInfinite ~num)) + :added "1.11"} + [^double num] + (Double/isInfinite num)) diff --git a/src/clj/clojure/core/server.clj b/src/clj/clojure/core/server.clj index f343aa76fe..3cda4d1f74 100644 --- a/src/clj/clojure/core/server.clj +++ b/src/clj/clojure/core/server.clj @@ -51,7 +51,7 @@ "Validate server config options" [{:keys [name port accept] :as opts}] (doseq [prop [:name :port :accept]] (required opts prop)) - (when (or (not (integer? port)) (not (< -1 port 65535))) + (when (or (not (integer? port)) (not (<= 0 port 65535))) (throw (ex-info (str "Invalid socket server port: " port) opts)))) (defn- accept-connection @@ -288,7 +288,7 @@ (try (assoc m :val (valf (:val m))) (catch Throwable ex - (assoc m :val (ex->data ex :print-eval-result) + (assoc m :val (valf (ex->data ex :print-eval-result)) :exception true))) m)))))))) diff --git a/src/clj/clojure/core_deftype.clj b/src/clj/clojure/core_deftype.clj index 786f0d4b53..c2babab291 100644 --- a/src/clj/clojure/core_deftype.clj +++ b/src/clj/clojure/core_deftype.clj @@ -67,8 +67,9 @@ (throw (IllegalArgumentException. (apply print-str "Unsupported option(s) -" bad-opts)))) [interfaces methods opts])) -(defmacro reify - "reify is a macro with the following structure: +(defmacro reify + "reify creates an object implementing a protocol or interface. + reify is a macro with the following structure: (reify options* specs*) @@ -651,7 +652,15 @@ [opts sigs])) sigs (when sigs (reduce1 (fn [m s] - (let [name-meta (meta (first s)) + (let [tag-to-class (fn [tag] + (if-let [c (and (instance? clojure.lang.Symbol tag) + (= (.indexOf (.getName ^clojure.lang.Symbol tag) ".") -1) + (not (contains? '#{int long float double char short byte boolean void + ints longs floats doubles chars shorts bytes booleans objects} tag)) + (resolve tag))] + (symbol (.getName c)) + tag)) + name-meta (update-in (meta (first s)) [:tag] tag-to-class) mname (with-meta (first s) nil) [arglists doc] (loop [as [] rs (rest s)] diff --git a/src/clj/clojure/core_proxy.clj b/src/clj/clojure/core_proxy.clj index c7f35774e6..46f7b4b868 100644 --- a/src/clj/clojure/core_proxy.clj +++ b/src/clj/clojure/core_proxy.clj @@ -241,12 +241,17 @@ mb (map #(vector (%1 %2) (vals (dissoc %1 %2))) mgroups rtypes) bridge? (reduce1 into1 #{} (map second mb)) ifaces-meths (remove bridge? (vals ifaces-meths)) - mm (remove bridge? (vals mm))] + mm (remove bridge? (vals mm)) + reflect-Method-keyfn (fn [meth] + (let [[name param-types ^Class return-type] (method-sig meth)] + (-> [name] + (into1 (map #(.getName ^Class %) param-types)) + (conj (.getName return-type)))))] ;add methods matching supers', if no mapping -> call super - (doseq [[^java.lang.reflect.Method dest bridges] mb - ^java.lang.reflect.Method meth bridges] + (doseq [[^java.lang.reflect.Method dest bridges] (sort-by (comp reflect-Method-keyfn first) mb) + ^java.lang.reflect.Method meth (sort-by reflect-Method-keyfn bridges)] (gen-bridge meth dest)) - (doseq [^java.lang.reflect.Method meth mm] + (doseq [^java.lang.reflect.Method meth (sort-by reflect-Method-keyfn mm)] (gen-method meth (fn [^GeneratorAdapter gen ^Method m] (. gen (loadThis)) @@ -259,7 +264,7 @@ (. m (getDescriptor))))))) ;add methods matching interfaces', if no mapping -> throw - (doseq [^java.lang.reflect.Method meth ifaces-meths] + (doseq [^java.lang.reflect.Method meth (sort-by reflect-Method-keyfn ifaces-meths)] (gen-method meth (fn [^GeneratorAdapter gen ^Method m] (. gen (throwException ex-type (. m (getName)))))))) diff --git a/src/clj/clojure/java/browse.clj b/src/clj/clojure/java/browse.clj index 6fcc650756..6a16ce37db 100644 --- a/src/clj/clojure/java/browse.clj +++ b/src/clj/clojure/java/browse.clj @@ -12,7 +12,9 @@ clojure.java.browse (:require [clojure.java.shell :as sh] [clojure.string :as str]) - (:import (java.net URI))) + (:import (java.io File) + (java.net URI) + (java.lang ProcessBuilder ProcessBuilder$Redirect))) (defn- macosx? [] (-> "os.name" System/getProperty .toLowerCase @@ -71,6 +73,16 @@ script (if (= :uninitialized script) (reset! *open-url-script* (open-url-script-val)) script)] - (or (when script (sh/sh script (str url)) true) + (or (when script + (try + (let [command [script (str url)] + null-file (File. (if (.startsWith (System/getProperty "os.name") "Windows") "NUL" "/dev/null")) + pb (doto (ProcessBuilder. ^java.util.List command) + ;; emulate ProcessBuilder.Redirect.DISCARD added in Java 9 + (.redirectOutput null-file) + (.redirectError null-file))] + (.start pb) ;; do not wait for the process + true) + (catch Throwable _ false))) (open-url-in-browser url) (open-url-in-swing url)))) diff --git a/src/clj/clojure/math.clj b/src/clj/clojure/math.clj new file mode 100644 index 0000000000..c5b67b1f1c --- /dev/null +++ b/src/clj/clojure/math.clj @@ -0,0 +1,523 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns + ^{:author "Alex Miller", + :doc "Clojure wrapper functions for java.lang.Math static methods. + + Function calls are inlined for performance, and type hinted for primitive + long or double parameters where appropriate. In general, Math methods are + optimized for performance and have bounds for error tolerance. If + greater precision is needed, use java.lang.StrictMath directly instead. + + For more complete information, see: + https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html"} + clojure.math) + +(set! *warn-on-reflection* true) + +(def + ^{:doc "Constant for e, the base for natural logarithms. + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#E" + :added "1.11" + :const true + :tag 'double} + E + Math/E) + +(def + ^{:doc "Constant for pi, the ratio of the circumference of a circle to its diameter. + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#PI" + :added "1.11" + :const true + :tag 'double} + PI + Math/PI) + +(defn sin + {:doc "Returns the sine of an angle. + If a is ##NaN, ##-Inf, ##Inf => ##NaN + If a is zero => zero with the same sign as a + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#sin-double-" + :inline-arities #{1} + :inline (fn [a] `(Math/sin (double ~a))) + :added "1.11"} + ^double [^double a] + (Math/sin a)) + +(defn cos + {:doc "Returns the cosine of an angle. + If a is ##NaN, ##-Inf, ##Inf => ##NaN + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#cos-double-" + :inline-arities #{1} + :inline (fn [a] `(Math/cos (double ~a))) + :added "1.11"} + ^double [^double a] + (Math/cos a)) + +(defn tan + {:doc "Returns the tangent of an angle. + If a is ##NaN, ##-Inf, ##Inf => ##NaN + If a is zero => zero with the same sign as a + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#tan-double-" + :inline-arities #{1} + :inline (fn [a] `(Math/tan (double ~a))) + :added "1.11"} + ^double [^double a] + (Math/tan a)) + +(defn asin + {:doc "Returns the arc sine of an angle, in the range -pi/2 to pi/2. + If a is ##NaN or |a|>1 => ##NaN + If a is zero => zero with the same sign as a + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#asin-double-" + :inline-arities #{1} + :inline (fn [a] `(Math/asin (double ~a))) + :added "1.11"} + ^double [^double a] + (Math/asin a)) + +(defn acos + {:doc "Returns the arc cosine of a, in the range 0.0 to pi. + If a is ##NaN or |a|>1 => ##NaN + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#acos-double-" + :inline-arities #{1} + :inline (fn [a] `(Math/acos (double ~a))) + :added "1.11"} + ^double [^double a] + (Math/acos a)) + +(defn atan + {:doc "Returns the arc tangent of a, in the range of -pi/2 to pi/2. + If a is ##NaN => ##NaN + If a is zero => zero with the same sign as a + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#atan-double-" + :inline-arities #{1} + :inline (fn [a] `(Math/atan (double ~a))) + :added "1.11"} + ^double [^double a] + (Math/atan a)) + +(defn to-radians + {:doc "Converts an angle in degrees to an approximate equivalent angle in radians. + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#toRadians-double-" + :inline-arities #{1} + :inline (fn [deg] `(Math/toRadians (double ~deg))) + :added "1.11"} + ^double [^double deg] + (Math/toRadians deg)) + +(defn to-degrees + {:doc "Converts an angle in radians to an approximate equivalent angle in degrees. + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#toDegrees-double-" + :inline-arities #{1} + :inline (fn [r] `(Math/toDegrees (double ~r))) + :added "1.11"} + ^double [^double r] + (Math/toDegrees r)) + +(defn exp + {:doc "Returns Euler's number e raised to the power of a. + If a is ##NaN => ##NaN + If a is ##Inf => ##Inf + If a is ##-Inf => +0.0 + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#exp-double-" + :inline-arities #{1} + :inline (fn [a] `(Math/exp (double ~a))) + :added "1.11"} + ^double [^double a] + (Math/exp a)) + +(defn log + {:doc "Returns the natural logarithm (base e) of a. + If a is ##NaN or negative => ##NaN + If a is ##Inf => ##Inf + If a is zero => ##-Inf + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#log-double-" + :inline-arities #{1} + :inline (fn [a] `(Math/log (double ~a))) + :added "1.11"} + ^double [^double a] + (Math/log a)) + +(defn log10 + {:doc "Returns the logarithm (base 10) of a. + If a is ##NaN or negative => ##NaN + If a is ##Inf => ##Inf + If a is zero => ##-Inf + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#log10-double-" + :inline-arities #{1} + :inline (fn [a] `(Math/log10 (double ~a))) + :added "1.11"} + ^double [^double a] + (Math/log10 a)) + +(defn sqrt + {:doc "Returns the positive square root of a. + If a is ##NaN or negative => ##NaN + If a is ##Inf => ##Inf + If a is zero => a + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#sqrt-double-" + :inline-arities #{1} + :inline (fn [a] `(Math/sqrt (double ~a))) + :added "1.11"} + ^double [^double a] + (Math/sqrt a)) + +(defn cbrt + {:doc "Returns the cube root of a. + If a is ##NaN => ##NaN + If a is ##Inf or ##-Inf => a + If a is zero => zero with sign matching a + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#cbrt-double-" + :inline-arities #{1} + :inline (fn [a] `(Math/cbrt (double ~a))) + :added "1.11"} + ^double [^double a] + (Math/cbrt a)) + +(defn IEEE-remainder + {:doc "Returns the remainder per IEEE 754 such that + remainder = dividend - divisor * n + where n is the integer closest to the exact value of dividend / divisor. + If two integers are equally close, then n is the even one. + If the remainder is zero, sign will match dividend. + If dividend or divisor is ##NaN, or dividend is ##Inf or ##-Inf, or divisor is zero => ##NaN + If dividend is finite and divisor is infinite => dividend + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#IEEEremainder-double-double-" + :inline-arities #{2} + :inline (fn [dividend divisor] `(Math/IEEEremainder (double ~dividend) (double ~divisor))) + :added "1.11"} + ^double [^double dividend ^double divisor] + (Math/IEEEremainder dividend divisor)) + +(defn ceil + {:doc "Returns the smallest double greater than or equal to a, and equal to a + mathematical integer. + If a is ##NaN or ##Inf or ##-Inf or already equal to an integer => a + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#ceil-double-" + :inline-arities #{1} + :inline (fn [a] `(Math/ceil (double ~a))) + :added "1.11"} + ^double [^double a] + (Math/ceil a)) + +(defn floor + {:doc "Returns the largest double less than or equal to a, and equal to a + mathematical integer. + If a is ##NaN or ##Inf or ##-Inf or already equal to an integer => a + If a is less than zero but greater than -1.0 => -0.0 + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#floor-double-" + :inline-arities #{1} + :inline (fn [a] `(Math/floor (double ~a))) + :added "1.11"} + ^double [^double a] + (Math/floor a)) + +(defn rint + {:doc "Returns the double closest to a and equal to a mathematical integer. + If two values are equally close, return the even one. + If a is ##NaN or ##Inf or ##-Inf or zero => a + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#rint-double-" + :inline-arities #{1} + :inline (fn [a] `(Math/rint (double ~a))) + :added "1.11"} + ^double [^double a] + (Math/rint a)) + +(defn atan2 + {:doc "Returns the angle theta from the conversion of rectangular coordinates (x, y) to polar coordinates (r, theta). + Computes the phase theta by computing an arc tangent of y/x in the range of -pi to pi. + For more details on special cases, see: + https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#atan2-double-double-" + :inline-arities #{2} + :inline (fn [y x] `(Math/atan2 (double ~y) (double ~x))) + :added "1.11"} + ^double [^double y ^double x] + (Math/atan2 y x)) + +(defn pow + {:doc "Returns the value of a raised to the power of b. + For more details on special cases, see: + https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#pow-double-double-" + :inline-arities #{2} + :inline (fn [a b] `(Math/pow (double ~a) (double ~b))) + :added "1.11"} + ^double [^double a ^double b] + (Math/pow a b)) + +(defn round + {:doc "Returns the closest long to a. If equally close to two values, return the one + closer to ##Inf. + If a is ##NaN => 0 + If a is ##-Inf or < Long/MIN_VALUE => Long/MIN_VALUE + If a is ##Inf or > Long/MAX_VALUE => Long/MAX_VALUE + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#round-double-" + :inline-arities #{1} + :inline (fn [a] `(Math/round (double ~a))) + :added "1.11"} + ^long [^double a] + (Math/round a)) + +(defn random + {:doc "Returns a positive double between 0.0 and 1.0, chosen pseudorandomly with + approximately random distribution. + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#random--" + :inline-arities #{0} + :inline (fn [] `(Math/random)) + :added "1.11"} + ^double [] + (Math/random)) + +(defn add-exact + {:doc "Returns the sum of x and y, throws ArithmeticException on overflow. + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#addExact-long-long-" + :inline-arities #{2} + :inline (fn [x y] `(Math/addExact (long ~x) (long ~y))) + :added "1.11"} + ^long [^long x ^long y] + (Math/addExact x y)) + +(defn subtract-exact + {:doc "Returns the difference of x and y, throws ArithmeticException on overflow. + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#subtractExact-long-long-" + :inline-arities #{2} + :inline (fn [x y] `(Math/subtractExact (long ~x) (long ~y))) + :added "1.11"} + ^long [^long x ^long y] + (Math/subtractExact x y)) + +(defn multiply-exact + {:doc "Returns the product of x and y, throws ArithmeticException on overflow. + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#multiplyExact-long-long-" + :inline-arities #{2} + :inline (fn [x y] `(Math/multiplyExact (long ~x) (long ~y))) + :added "1.11"} + ^long [^long x ^long y] + (Math/multiplyExact x y)) + +(defn increment-exact + {:doc "Returns a incremented by 1, throws ArithmeticException on overflow. + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#incrementExact-long-" + :inline-arities #{1} + :inline (fn [a] `(Math/incrementExact (long ~a))) + :added "1.11"} + ^long [^long a] + (Math/incrementExact a)) + +(defn decrement-exact + {:doc "Returns a decremented by 1, throws ArithmeticException on overflow. + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#decrementExact-long-" + :inline-arities #{1} + :inline (fn [a] `(Math/decrementExact (long ~a))) + :added "1.11"} + ^long [^long a] + (Math/decrementExact a)) + +(defn negate-exact + {:doc "Returns the negation of a, throws ArithmeticException on overflow. + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#negateExact-long-" + :inline-arities #{1} + :inline (fn [a] `(Math/negateExact (long ~a))) + :added "1.11"} + ^long [^long a] + (Math/negateExact a)) + +(defn floor-div + {:doc "Integer division that rounds to negative infinity (as opposed to zero). + The special case (floorDiv Long/MIN_VALUE -1) overflows and returns Long/MIN_VALUE. + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#floorDiv-long-long-" + :inline-arities #{2} + :inline (fn [x y] `(Math/floorDiv (long ~x) (long ~y))) + :added "1.11"} + ^long [^long x ^long y] + (Math/floorDiv x y)) + +(defn floor-mod + {:doc "Integer modulus x - (floorDiv(x, y) * y). Sign matches y and is in the + range -|y| < r < |y|. + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#floorMod-long-long-" + :inline-arities #{2} + :inline (fn [x y] `(Math/floorMod (long ~x) (long ~y))) + :added "1.11"} + ^long [^long x ^long y] + (Math/floorMod x y)) + +(defn ulp + {:doc "Returns the size of an ulp (unit in last place) for d. + If d is ##NaN => ##NaN + If d is ##Inf or ##-Inf => ##Inf + If d is zero => Double/MIN_VALUE + If d is +/- Double/MAX_VALUE => 2^971 + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#ulp-double-" + :inline-arities #{1} + :inline (fn [d] `(Math/ulp (double ~d))) + :added "1.11"} + ^double [^double d] + (Math/ulp d)) + +(defn signum + {:doc "Returns the signum function of d - zero for zero, 1.0 if >0, -1.0 if <0. + If d is ##NaN => ##NaN + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#signum-double-" + :inline-arities #{1} + :inline (fn [d] `(Math/signum (double ~d))) + :added "1.11"} + ^double [^double d] + (Math/signum d)) + +(defn sinh + {:doc "Returns the hyperbolic sine of x, (e^x - e^-x)/2. + If x is ##NaN => ##NaN + If x is ##Inf or ##-Inf or zero => x + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#sinh-double-" + :inline-arities #{1} + :inline (fn [x] `(Math/sinh (double ~x))) + :added "1.11"} + ^double [^double x] + (Math/sinh x)) + +(defn cosh + {:doc "Returns the hyperbolic cosine of x, (e^x + e^-x)/2. + If x is ##NaN => ##NaN + If x is ##Inf or ##-Inf => ##Inf + If x is zero => 1.0 + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#cosh-double-" + :inline-arities #{1} + :inline (fn [x] `(Math/cosh (double ~x))) + :added "1.11"} + ^double [^double x] + (Math/cosh x)) + +(defn tanh + {:doc "Returns the hyperbolic tangent of x, sinh(x)/cosh(x). + If x is ##NaN => ##NaN + If x is zero => zero, with same sign + If x is ##Inf => +1.0 + If x is ##-Inf => -1.0 + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#tanh-double-" + :inline-arities #{1} + :inline (fn [x] `(Math/tanh (double ~x))) + :added "1.11"} + ^double [^double x] + (Math/tanh x)) + +(defn hypot + {:doc "Returns sqrt(x^2 + y^2) without intermediate underflow or overflow. + If x or y is ##Inf or ##-Inf => ##Inf + If x or y is ##NaN and neither is ##Inf or ##-Inf => ##NaN + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#hypot-double-double-" + :inline-arities #{2} + :inline (fn [x y] `(Math/hypot (double ~x) (double ~y))) + :added "1.11"} + ^double [^double x ^double y] + (Math/hypot x y)) + +(defn expm1 + {:doc "Returns e^x - 1. Near 0, expm1(x)+1 is more accurate to e^x than exp(x). + If x is ##NaN => ##NaN + If x is ##Inf => #Inf + If x is ##-Inf => -1.0 + If x is zero => x + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#expm1-double-" + :inline-arities #{1} + :inline (fn [x] `(Math/expm1 (double ~x))) + :added "1.11"} + ^double [^double x] + (Math/expm1 x)) + +(defn log1p + {:doc "Returns ln(1+x). For small values of x, log1p(x) is more accurate than + log(1.0+x). + If x is ##NaN or < -1 => ##NaN + If x is ##Inf => ##Inf + If x is -1 => ##-Inf + If x is 0 => 0 with sign matching x + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#log1p-double-" + :inline-arities #{1} + :inline (fn [x] `(Math/log1p (double ~x))) + :added "1.11"} + ^double [^double x] + (Math/log1p x)) + +(defn copy-sign + {:doc "Returns a double with the magnitude of the first argument and the sign of + the second. + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#copySign-double-double-" + :inline-arities #{2} + :inline (fn [magnitude sign] `(Math/copySign (double ~magnitude) (double ~sign))) + :added "1.11"} + ^double [^double magnitude ^double sign] + (Math/copySign magnitude sign)) + +(defn get-exponent + {:doc "Returns the exponent of d. + If d is ##NaN, ##Inf, ##-Inf => Double/MAX_EXPONENT + 1 + If d is zero or subnormal => Double/MIN_EXPONENT - 1 + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#getExponent-double-" + :inline-arities #{1} + :inline (fn [d] `(Math/getExponent (double ~d))) + :added "1.11"} + [^double d] + (Math/getExponent d)) + +(defn next-after + {:doc "Returns the adjacent floating point number to start in the direction of + the second argument. If the arguments are equal, the second is returned. + If either arg is #NaN => #NaN + If both arguments are signed zeros => direction + If start is +-Double/MIN_VALUE and direction would cause a smaller magnitude + => zero with sign matching start + If start is ##Inf or ##-Inf and direction would cause a smaller magnitude + => Double/MAX_VALUE with same sign as start + If start is equal to +=Double/MAX_VALUE and direction would cause a larger magnitude + => ##Inf or ##-Inf with sign matching start + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextAfter-double-double-" + :inline-arities #{2} + :inline (fn [start direction] `(Math/nextAfter (double ~start) (double ~direction))) + :added "1.11"} + ^double [^double start ^double direction] + (Math/nextAfter start direction)) + +(defn next-up + {:doc "Returns the adjacent double of d in the direction of ##Inf. + If d is ##NaN => ##NaN + If d is ##Inf => ##Inf + If d is zero => Double/MIN_VALUE + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextUp-double-" + :inline-arities #{1} + :inline (fn [d] `(Math/nextUp (double ~d))) + :added "1.11"} + ^double [^double d] + (Math/nextUp d)) + +(defn next-down + {:doc "Returns the adjacent double of d in the direction of ##-Inf. + If d is ##NaN => ##NaN + If d is ##-Inf => ##-Inf + If d is zero => -Double/MIN_VALUE + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextDown-double-" + :inline-arities #{1} + :inline (fn [d] `(Math/nextDown (double ~d))) + :added "1.11"} + ^double [^double d] + (Math/nextDown d)) + +(defn scalb + {:doc "Returns d * 2^scaleFactor, scaling by a factor of 2. If the exponent + is between Double/MIN_EXPONENT and Double/MAX_EXPONENT, the answer is exact. + If d is ##NaN => ##NaN + If d is ##Inf or ##-Inf => ##Inf or ##-Inf respectively + If d is zero => zero of same sign as d + See: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextDown-double-" + :inline-arities #{2} + :inline (fn [d scaleFactor] `(Math/scalb (double ~d) (int ~scaleFactor))) + :added "1.11"} + ^double [^double d scaleFactor] + (Math/scalb d (int scaleFactor))) + diff --git a/src/clj/clojure/set.clj b/src/clj/clojure/set.clj index b63a004475..9ffb72900a 100644 --- a/src/clj/clojure/set.clj +++ b/src/clj/clojure/set.clj @@ -106,7 +106,11 @@ (defn map-invert "Returns the map with the vals mapped to the keys." {:added "1.0"} - [m] (reduce (fn [m [k v]] (assoc m v k)) {} m)) + [m] + (persistent! + (reduce-kv (fn [m k v] (assoc! m v k)) + (transient {}) + m))) (defn join "When passed 2 rels, returns the rel corresponding to the natural diff --git a/src/clj/clojure/string.clj b/src/clj/clojure/string.clj index 35e0650f65..38f0d6d91d 100644 --- a/src/clj/clojure/string.clj +++ b/src/clj/clojure/string.clj @@ -218,7 +218,8 @@ Design notes for clojure.string: (defn split "Splits string on a regular expression. Optional argument limit is - the maximum number of splits. Not lazy. Returns vector of the splits." + the maximum number of parts. Not lazy. Returns vector of the parts. + Trailing empty strings are not returned - pass limit of -1 to return all." {:added "1.2"} ([^CharSequence s ^Pattern re] (LazilyPersistentVector/createOwning (.split re s))) @@ -226,7 +227,7 @@ Design notes for clojure.string: (LazilyPersistentVector/createOwning (.split re s limit)))) (defn split-lines - "Splits s on \\n or \\r\\n." + "Splits s on \\n or \\r\\n. Trailing empty lines are not returned." {:added "1.2"} [^CharSequence s] (split s #"\r?\n")) diff --git a/src/clj/clojure/test.clj b/src/clj/clojure/test.clj index fcb3224846..fc3536d7f3 100644 --- a/src/clj/clojure/test.clj +++ b/src/clj/clojure/test.clj @@ -447,7 +447,7 @@ result# (apply ~pred values#)] (if result# (do-report {:type :pass, :message ~msg, - :expected '~form, :actual (cons ~pred values#)}) + :expected '~form, :actual (cons '~pred values#)}) (do-report {:type :fail, :message ~msg, :expected '~form, :actual (list '~'not (cons '~pred values#))})) result#))) @@ -721,8 +721,8 @@ (do-report {:type :end-test-var, :var v})))) (defn test-vars - "Groups vars by their namespace and runs test-vars on them with - appropriate fixtures applied." + "Groups vars by their namespace and runs test-var on them with + appropriate fixtures applied." {:added "1.6"} [vars] (doseq [[ns vars] (group-by (comp :ns meta) vars)] @@ -793,3 +793,38 @@ [summary] (and (zero? (:fail summary 0)) (zero? (:error summary 0)))) + +(defn run-test-var + "Runs the tests for a single Var, with fixtures executed around the test, and summary output after." + {:added "1.11"} + [v] + (binding [*report-counters* (ref *initial-report-counters*)] + (let [ns-obj (-> v meta :ns) + summary (do + (do-report {:type :begin-test-ns + :ns ns-obj}) + (test-vars [v]) + (do-report {:type :end-test-ns + :ns ns-obj}) + (assoc @*report-counters* :type :summary))] + (do-report summary) + summary))) + +(defmacro run-test + "Runs a single test. + + Because the intent is to run a single test, there is no check for the namespace test-ns-hook." + {:added "1.11"} + [test-symbol] + (let [test-var (resolve test-symbol)] + (cond + (nil? test-var) + (binding [*out* *err*] + (println "Unable to resolve" test-symbol "to a test function.")) + + (not (-> test-var meta :test)) + (binding [*out* *err*] + (println test-symbol "is not a test.")) + + :else + `(run-test-var ~test-var)))) diff --git a/src/clj/clojure/xml.clj b/src/clj/clojure/xml.clj index 4e4220f265..d892e19279 100644 --- a/src/clj/clojure/xml.clj +++ b/src/clj/clojure/xml.clj @@ -72,8 +72,36 @@ (skippedEntity [name]) )))) -(defn startparse-sax [s ch] - (.. SAXParserFactory (newInstance) (newSAXParser) (parse s ch))) +(defn sax-parser + "Create a new SAXParser" + {:added "1.11"} + ^SAXParser [] + (.newSAXParser (SAXParserFactory/newInstance))) + +(defn disable-external-entities + "Modifies a SAXParser to disable external entity resolution to prevent XXE attacks" + {:added "1.11"} + ^SAXParser [^SAXParser parser] + (let [reader (.getXMLReader parser)] + ;; as per https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html + (.setFeature reader "http://apache.org/xml/features/nonvalidating/load-external-dtd" false) + (.setFeature reader "http://xml.org/sax/features/external-general-entities", false) + (.setFeature reader "http://xml.org/sax/features/external-parameter-entities" false) + parser)) + +(defn startparse-sax + "A startparse function suitable for use with clojure.xml/parse. + Note that this function is open to XXE entity attacks, see startparse-sax-safe." + {:added "1.0"} + [s ch] + (.parse (sax-parser) s ch)) + +(defn startparse-sax-safe + "A startparse function suitable for use with clojure.xml/parse. + External entity resolution is disabled to prevent XXE entity attacks." + {:added "1.11"} + [s ch] + (.parse (disable-external-entities (sax-parser)) s ch)) (defn parse "Parses and loads the source s, which can be a File, InputStream or @@ -81,9 +109,13 @@ which has the keys :tag, :attrs, and :content. and accessor fns tag, attrs, and content. Other parsers can be supplied by passing startparse, a fn taking a source and a ContentHandler and returning - a parser" + a parser. + + Prior to 1.11, used startparse-sax by default. As of 1.11, uses + startparse-sax-safe, which disables XXE (XML External Entity) + processing. Pass startparse-sax to revert to prior behavior." {:added "1.0"} - ([s] (parse s startparse-sax)) + ([s] (parse s startparse-sax-safe)) ([s startparse] (binding [*stack* nil *current* (struct element) diff --git a/src/jvm/clojure/java/api/Clojure.java b/src/jvm/clojure/java/api/Clojure.java index 3667b22791..8a9ef9b543 100644 --- a/src/jvm/clojure/java/api/Clojure.java +++ b/src/jvm/clojure/java/api/Clojure.java @@ -41,7 +41,7 @@ * require.invoke(Clojure.read("clojure.set")); * *

IFns can be passed to higher order functions, e.g. the - * example below passes plus to read:

+ * example below passes inc to map:

* *
  * IFn map = Clojure.var("clojure.core", "map");
diff --git a/src/jvm/clojure/java/api/package.html b/src/jvm/clojure/java/api/package.html
index 6536c33ab5..a24d3c9b79 100644
--- a/src/jvm/clojure/java/api/package.html
+++ b/src/jvm/clojure/java/api/package.html
@@ -57,7 +57,7 @@
 

IFns can be passed to higher order functions, e.g. the - example below passes plus to read: + example below passes inc to map:

 IFn map = Clojure.var("clojure.core", "map");
 IFn inc = Clojure.var("clojure.core", "inc");
diff --git a/src/jvm/clojure/lang/APersistentVector.java b/src/jvm/clojure/lang/APersistentVector.java
index c9f15cdd3f..3e88d14dbf 100644
--- a/src/jvm/clojure/lang/APersistentVector.java
+++ b/src/jvm/clojure/lang/APersistentVector.java
@@ -98,15 +98,18 @@ static boolean doEquiv(IPersistentVector v, Object obj){
 	else if(obj instanceof List)
 		{
 		Collection ma = (Collection) obj;
-		if(ma.size() != v.count())
+
+		if((!(ma instanceof IPersistentCollection) || (ma instanceof Counted)) && (ma.size() != v.count()))
 			return false;
-		for(Iterator i1 = ((List) v).iterator(), i2 = ma.iterator();
-		    i1.hasNext();)
+
+		Iterator i2 = ma.iterator();
+
+		for(Iterator i1 = ((List) v).iterator(); i1.hasNext();)
 			{
-			if(!Util.equiv(i1.next(), i2.next()))
+			if(!i2.hasNext() || !Util.equiv(i1.next(), i2.next()))
 				return false;
 			}
-		return true;
+		return !i2.hasNext();
 		}
 	else
 		{
@@ -544,7 +547,7 @@ public APersistentVector.RSeq withMeta(IPersistentMap meta){
 	}
 }
 
-public static class SubVector extends APersistentVector implements IObj{
+public static class SubVector extends APersistentVector implements IObj, IKVReduce{
 	public final IPersistentVector v;
 	public final int start;
 	public final int end;
@@ -574,6 +577,16 @@ public Iterator iterator(){
 		return super.iterator();
 	}
 
+	public Object kvreduce(IFn f, Object init){
+		int cnt = count();
+		for (int i=0; i= end) || (i < 0))
 			throw new IndexOutOfBoundsException();
diff --git a/src/jvm/clojure/lang/ASeq.java b/src/jvm/clojure/lang/ASeq.java
index 325aa27c24..f0f8e5d447 100644
--- a/src/jvm/clojure/lang/ASeq.java
+++ b/src/jvm/clojure/lang/ASeq.java
@@ -37,6 +37,11 @@ public boolean equiv(Object obj){
 
 	if(!(obj instanceof Sequential || obj instanceof List))
 		return false;
+
+	if(this instanceof Counted && obj instanceof Counted &&
+	   ((Counted)this).count() != ((Counted)obj).count())
+		return false;
+
 	ISeq ms = RT.seq(obj);
 	for(ISeq s = seq(); s != null; s = s.next(), ms = ms.next())
 		{
diff --git a/src/jvm/clojure/lang/ArraySeq.java b/src/jvm/clojure/lang/ArraySeq.java
index 597a5410de..5f54d87aaf 100644
--- a/src/jvm/clojure/lang/ArraySeq.java
+++ b/src/jvm/clojure/lang/ArraySeq.java
@@ -15,6 +15,9 @@
 import java.lang.reflect.Array;
 
 public class ArraySeq extends ASeq implements IndexedSeq, IReduce{
+
+private static final long serialVersionUID = -9069152683729302290L;
+
 public final Object[] array;
 final int i;
 //ISeq _rest;
@@ -142,6 +145,13 @@ public int lastIndexOf(Object o) {
 	return -1;
 }
 
+public Object[] toArray(){
+	int sz = this.array.length - this.i;
+	Object[] ret = new Object[sz];
+	System.arraycopy(this.array, i, ret, 0, sz);
+	return ret;
+}
+
 //////////////////////////////////// specialized primitive versions ///////////////////////////////
 
 static public class ArraySeq_int extends ASeq implements IndexedSeq, IReduce{
diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java
index 041786e888..12cf1e5623 100644
--- a/src/jvm/clojure/lang/Compiler.java
+++ b/src/jvm/clojure/lang/Compiler.java
@@ -1612,8 +1612,16 @@ public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
 				gen.invokeInterface(type, m);
 			else
 				gen.invokeVirtual(type, m);
-			//if(context != C.STATEMENT || method.getReturnType() == Void.TYPE)
-			HostExpr.emitBoxReturn(objx, gen, method.getReturnType());
+			Class retClass = method.getReturnType();
+			if(context == C.STATEMENT)
+				{
+				if(retClass == long.class || retClass == double.class)
+					gen.pop2();
+				else if(retClass != void.class)
+					gen.pop();
+				}
+			else
+				HostExpr.emitBoxReturn(objx, gen, retClass);
 			}
 		else
 			{
@@ -1627,9 +1635,9 @@ public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
 				method.emitClearLocals(gen);
 				}
 			gen.invokeStatic(REFLECTOR_TYPE, invokeInstanceMethodMethod);
+			if(context == C.STATEMENT)
+				gen.pop();
 			}
-		if(context == C.STATEMENT)
-			gen.pop();
 	}
 
 	public boolean hasJavaClass(){
@@ -6016,9 +6024,14 @@ public static class LocalBindingExpr implements Expr, MaybePrimitiveExpr, Assign
 	public LocalBindingExpr(LocalBinding b, Symbol tag)
             {
 		if(b.getPrimitiveType() != null && tag != null)
-			throw new UnsupportedOperationException("Can't type hint a primitive local");
+			if(! b.getPrimitiveType().equals(tagClass(tag)))
+				throw new UnsupportedOperationException("Can't type hint a primitive local with a different type");
+			else
+				this.tag = null;
+		else
+			this.tag = tag;
+
 		this.b = b;
-		this.tag = tag;
 
         this.clearPath = (PathNode)CLEAR_PATH.get();
         this.clearRoot = (PathNode)CLEAR_ROOT.get();
@@ -7649,7 +7662,7 @@ public static Object load(Reader rdr, String sourcePath, String sourceName) {
 	catch(Throwable e)
 		{
 		if(!(e instanceof CompilerException))
-			throw new CompilerException(sourcePath, (Integer) LINE_BEFORE.deref(), (Integer) COLUMN_BEFORE.deref(), e);
+			throw new CompilerException(sourcePath, (Integer) LINE_BEFORE.deref(), (Integer) COLUMN_BEFORE.deref(), null, CompilerException.PHASE_EXECUTION, e);
 		else
 			throw (CompilerException) e;
 		}
diff --git a/src/jvm/clojure/lang/Cycle.java b/src/jvm/clojure/lang/Cycle.java
index 877695558a..0be07b5949 100644
--- a/src/jvm/clojure/lang/Cycle.java
+++ b/src/jvm/clojure/lang/Cycle.java
@@ -93,4 +93,13 @@ public Object reduce(IFn f, Object start){
             s = all;
     }
 }
+
+public int hashCode(){
+    throw new UnsupportedOperationException();
+}
+
+public int hasheq(){
+    throw new UnsupportedOperationException();
+}
+
 }
diff --git a/src/jvm/clojure/lang/Delay.java b/src/jvm/clojure/lang/Delay.java
index 262c9c1a43..ffd418962d 100644
--- a/src/jvm/clojure/lang/Delay.java
+++ b/src/jvm/clojure/lang/Delay.java
@@ -54,7 +54,7 @@ public Object deref() {
 	return val;
 }
 
-synchronized public boolean isRealized(){
+public boolean isRealized(){
 	return fn == null;
 }
 }
diff --git a/src/jvm/clojure/lang/Iterate.java b/src/jvm/clojure/lang/Iterate.java
index aec0c14aa2..5aaf9cbb52 100644
--- a/src/jvm/clojure/lang/Iterate.java
+++ b/src/jvm/clojure/lang/Iterate.java
@@ -84,4 +84,12 @@ public Object reduce(IFn rf, Object start){
         v = f.invoke(v);
     }
 }
+
+public int hashCode(){
+    throw new UnsupportedOperationException();
+}
+
+public int hasheq(){
+    throw new UnsupportedOperationException();
+}
 }
diff --git a/src/jvm/clojure/lang/Keyword.java b/src/jvm/clojure/lang/Keyword.java
index 02d21326a2..253f455e4a 100644
--- a/src/jvm/clojure/lang/Keyword.java
+++ b/src/jvm/clojure/lang/Keyword.java
@@ -23,6 +23,8 @@
 
 public class Keyword implements IFn, Comparable, Named, Serializable, IHashEq {
 
+private static final long serialVersionUID = -2105088845257724163L;
+
 private static ConcurrentHashMap> table = new ConcurrentHashMap();
 static final ReferenceQueue rq = new ReferenceQueue();
 public final Symbol sym;
@@ -93,13 +95,21 @@ public String toString(){
 	return _str;
 }
 
+/**
+ * @deprecated CLJ-2350: This function is no longer called, but has not been
+ * removed to maintain the public interface.
+ */
 public Object throwArity(){
 	throw new IllegalArgumentException("Wrong number of args passed to keyword: "
 	                                   + toString());
 }
 
+Object throwArity(int n) {
+	throw new ArityException(n, toString());
+}
+
 public Object call() {
-	return throwArity();
+	return throwArity(0);
 }
 
 public void run(){
@@ -107,7 +117,7 @@ public void run(){
 }
 
 public Object invoke() {
-	return throwArity();
+	return throwArity(0);
 }
 
 public int compareTo(Object o){
@@ -146,98 +156,98 @@ final public Object invoke(Object obj, Object notFound) {
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3) {
-	return throwArity();
+	return throwArity(3);
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4) {
-	return throwArity();
+	return throwArity(4);
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
-	return throwArity();
+	return throwArity(5);
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
-	return throwArity();
+	return throwArity(6);
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7)
 		{
-	return throwArity();
+	return throwArity(7);
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8) {
-	return throwArity();
+	return throwArity(8);
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9) {
-	return throwArity();
+	return throwArity(9);
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10) {
-	return throwArity();
+	return throwArity(10);
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11) {
-	return throwArity();
+	return throwArity(11);
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11, Object arg12) {
-	return throwArity();
+	return throwArity(12);
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13)
 		{
-	return throwArity();
+	return throwArity(13);
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14)
 		{
-	return throwArity();
+	return throwArity(14);
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
                      Object arg15) {
-	return throwArity();
+	return throwArity(15);
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
                      Object arg15, Object arg16) {
-	return throwArity();
+	return throwArity(16);
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
                      Object arg15, Object arg16, Object arg17) {
-	return throwArity();
+	return throwArity(17);
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
                      Object arg15, Object arg16, Object arg17, Object arg18) {
-	return throwArity();
+	return throwArity(18);
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
                      Object arg15, Object arg16, Object arg17, Object arg18, Object arg19) {
-	return throwArity();
+	return throwArity(19);
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
                      Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20)
 		{
-	return throwArity();
+	return throwArity(20);
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
@@ -245,7 +255,7 @@ public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object
                      Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20,
                      Object... args)
 		{
-	return throwArity();
+	return throwArity(20 + args.length);
 }
 
 
diff --git a/src/jvm/clojure/lang/MultiFn.java b/src/jvm/clojure/lang/MultiFn.java
index fd37e2a085..44ac1ce1c0 100644
--- a/src/jvm/clojure/lang/MultiFn.java
+++ b/src/jvm/clojure/lang/MultiFn.java
@@ -85,7 +85,7 @@ public MultiFn preferMethod(Object dispatchValX, Object dispatchValY) {
 	rw.writeLock().lock();
 	try
 		{
-		if(prefers(dispatchValY, dispatchValX))
+		if(prefers(hierarchy.deref(), dispatchValY, dispatchValX))
 			throw new IllegalStateException(
 					String.format("Preference conflict in multimethod '%s': %s is already preferred to %s",
 					              name, dispatchValY, dispatchValX));
@@ -102,29 +102,29 @@ public MultiFn preferMethod(Object dispatchValX, Object dispatchValY) {
 		}
 }
 
-private boolean prefers(Object x, Object y) {
+private boolean prefers(Object hierarchy, Object x, Object y) {
 	IPersistentSet xprefs = (IPersistentSet) getPreferTable().valAt(x);
 	if(xprefs != null && xprefs.contains(y))
 		return true;
-	for(ISeq ps = RT.seq(parents.invoke(y)); ps != null; ps = ps.next())
+	for(ISeq ps = RT.seq(parents.invoke(hierarchy, y)); ps != null; ps = ps.next())
 		{
-		if(prefers(x, ps.first()))
+		if(prefers(hierarchy, x, ps.first()))
 			return true;
 		}
-	for(ISeq ps = RT.seq(parents.invoke(x)); ps != null; ps = ps.next())
+	for(ISeq ps = RT.seq(parents.invoke(hierarchy, x)); ps != null; ps = ps.next())
 		{
-		if(prefers(ps.first(), y))
+		if(prefers(hierarchy, ps.first(), y))
 			return true;
 		}
 	return false;
 }
 
-private boolean isA(Object x, Object y) {
-    return RT.booleanCast(isa.invoke(hierarchy.deref(), x, y));
+private boolean isA(Object hierarchy, Object x, Object y) {
+    return RT.booleanCast(isa.invoke(hierarchy, x, y));
 }
 
-private boolean dominates(Object x, Object y) {
-	return prefers(x, y) || isA(x, y);
+private boolean dominates(Object hierarchy, Object x, Object y) {
+	return prefers(hierarchy, x, y) || isA(hierarchy, x, y);
 }
 
 private IPersistentMap resetCache() {
@@ -170,11 +170,11 @@ private IFn findAndCacheBestMethod(Object dispatchVal) {
 		for(Object o : getMethodTable())
 			{
 			Map.Entry e = (Map.Entry) o;
-			if(isA(dispatchVal, e.getKey()))
+			if(isA(ch, dispatchVal, e.getKey()))
 				{
-				if(bestEntry == null || dominates(e.getKey(), bestEntry.getKey()))
+				if(bestEntry == null || dominates(ch, e.getKey(), bestEntry.getKey()))
 					bestEntry = e;
-				if(!dominates(bestEntry.getKey(), e.getKey()))
+				if(!dominates(ch, bestEntry.getKey(), e.getKey()))
 					throw new IllegalArgumentException(
 							String.format(
 									"Multiple methods in multimethod '%s' match dispatch value: %s -> %s and %s, and neither is preferred",
diff --git a/src/jvm/clojure/lang/Numbers.java b/src/jvm/clojure/lang/Numbers.java
index fa13cba62c..82d8e6914c 100644
--- a/src/jvm/clojure/lang/Numbers.java
+++ b/src/jvm/clojure/lang/Numbers.java
@@ -68,6 +68,8 @@ static interface Ops{
 	public Number dec(Number x);
 	public Number decP(Number x);
 	public Number unchecked_dec(Number x);
+
+	public Number abs(Number x);
 }
 
 static abstract class OpsP implements Ops{
@@ -619,6 +621,10 @@ public Number unchecked_dec(Number x){
 		long val = x.longValue();
 		return num(Numbers.unchecked_dec(val));
 	}
+
+	public Number abs(Number x){
+		return num(Math.abs(x.longValue()));
+	}
 }
 
 final static class DoubleOps extends OpsP{
@@ -706,6 +712,10 @@ public Number inc(Number x){
 	public Number dec(Number x){
 		return Double.valueOf(x.doubleValue() - 1);
 	}
+
+	public Number abs(Number x) {
+		return num(Math.abs(x.doubleValue()));
+	}
 }
 
 final static class RatioOps extends OpsP{
@@ -837,6 +847,11 @@ public Number dec(Number x){
 		return Numbers.add(x, -1);
 	}
 
+	public Number abs(Number x) {
+		Ratio r = (Ratio) x;
+		return new Ratio(r.numerator.abs(), r.denominator);
+	}
+
 }
 
 final static class BigIntOps extends OpsP{
@@ -935,6 +950,10 @@ public Number dec(Number x){
 		BigInteger bx = toBigInteger(x);
 		return BigInt.fromBigInteger(bx.subtract(BigInteger.ONE));
 	}
+
+	public Number abs(Number x) {
+		return BigInt.fromBigInteger(toBigInteger(x).abs());
+	}
 }
 
 
@@ -1054,6 +1073,14 @@ public Number dec(Number x){
 		       ? bx.subtract(BigDecimal.ONE)
 		       : bx.subtract(BigDecimal.ONE, mc);
 	}
+
+	public Number abs(Number x) {
+		MathContext mc = (MathContext) MATH_CONTEXT.deref();
+		BigDecimal bx = (BigDecimal) x;
+		return mc == null
+				? ((BigDecimal) x).abs()
+				: ((BigDecimal) x).abs(mc);
+	}
 }
 
 static final LongOps LONG_OPS = new LongOps();
@@ -1888,10 +1915,7 @@ static public Number unchecked_dec(Object x){
 static public double remainder(long x, double y){return remainder((double)x,y);}
 
 static public long add(long x, long y){
-	long ret = x + y;
-	if ((ret ^ x) < 0 && (ret ^ y) < 0)
-		return throwIntOverflow();
-	return ret;
+	return Math.addExact(x, y);
 }
 
 static public Number addP(long x, long y){
@@ -1902,10 +1926,7 @@ static public Number addP(long x, long y){
 }
 
 static public long minus(long x, long y){
-	long ret = x - y;
-	if (((ret ^ x) < 0 && (ret ^ ~y) < 0))
-		return throwIntOverflow();
-	return ret;
+	return Math.subtractExact(x, y);
 }
 
 static public Number minusP(long x, long y){
@@ -1916,9 +1937,7 @@ static public Number minusP(long x, long y){
 }
 
 static public long minus(long x){
-	if(x == Long.MIN_VALUE)
-		return throwIntOverflow();
-	return -x;
+	return Math.negateExact(x);
 }
 
 static public Number minusP(long x){
@@ -1928,9 +1947,7 @@ static public Number minusP(long x){
 }
 
 static public long inc(long x){
-	if(x == Long.MAX_VALUE)
-		return throwIntOverflow();
-	return x + 1;
+	return Math.incrementExact(x);
 }
 
 static public Number incP(long x){
@@ -1940,9 +1957,7 @@ static public Number incP(long x){
 }
 
 static public long dec(long x){
-	if(x == Long.MIN_VALUE)
-		return throwIntOverflow();
-	return x - 1;
+	return Math.decrementExact(x);
 }
 
 static public Number decP(long x){
@@ -1953,12 +1968,7 @@ static public Number decP(long x){
 
 
 static public long multiply(long x, long y){
-  if (x == Long.MIN_VALUE && y < 0)
-		return throwIntOverflow();
-	long ret = x * y;
-	if (y != 0 && ret/y != x)
-		return throwIntOverflow();
-	return ret;
+	return Math.multiplyExact(x, y);
 }
 
 static public Number multiplyP(long x, long y){
@@ -4068,11 +4078,7 @@ static public Object max(long x, double y){
 
 
 static public long max(long x, long y){
-	if(x > y) {
-		return x;
-	} else {
-		return y;
-	}
+	return Math.max(x, y);
 }
 
 
@@ -4166,11 +4172,7 @@ static public Object min(long x, double y){
 
 
 static public long min(long x, long y){
-	if(x < y) {
-		return x;
-	} else {
-		return y;
-	}
+	return Math.min(x, y);
 }
 
 static public Object min(long x, Object y){
@@ -4221,4 +4223,16 @@ static public Object min(Object x, Object y){
 	}
 }
 
+static public long abs(long x){
+	return Math.abs(x);
+}
+
+static public double abs(double x){
+	return Math.abs(x);
+}
+
+static public Number abs(Object x) {
+	return ops(x).abs((Number)x);
+}
+
 }
diff --git a/src/jvm/clojure/lang/PersistentArrayMap.java b/src/jvm/clojure/lang/PersistentArrayMap.java
index 27b860e5d5..1f86fd80aa 100644
--- a/src/jvm/clojure/lang/PersistentArrayMap.java
+++ b/src/jvm/clojure/lang/PersistentArrayMap.java
@@ -76,9 +76,61 @@ static public PersistentArrayMap createWithCheck(Object[] init){
 	return new PersistentArrayMap(init);
 }
 
+/**
+ * 

This method attempts to find resue the given array as the basis for an array map as quickly as possible.

+ * + *

If a trailing element exists in the array or it contains duplicate keys then it delegates to the complex path.

+ **/ static public PersistentArrayMap createAsIfByAssoc(Object[] init){ - if ((init.length & 1) == 1) - throw new IllegalArgumentException(String.format("No value supplied for key: %s", init[init.length-1])); + boolean complexPath, hasTrailing; + complexPath = hasTrailing = ((init.length & 1) == 1); + + for(int i=0;((i< init.length) && !complexPath);i += 2) + { + for(int j=0;jThis method handles the default case of an array containing alternating key/value pairs.

+ *

It will reallocate a smaller init array if duplicate keys are found.

+ * + *

If a trailing element is found then will attempt to add it to the resulting map as if by conj.

+ *

No guarantees about the order of the keys in the trailing element are made.

+ **/ +private static PersistentArrayMap createAsIfByAssocComplexPath(Object[] init, boolean hasTrailing){ + if(hasTrailing) + { + IPersistentCollection trailing = PersistentArrayMap.EMPTY.cons(init[init.length-1]); + init = growSeedArray(init, trailing); + } + // If this looks like it is doing busy-work, it is because it // is achieving these goals: O(n^2) run time like // createWithCheck(), never modify init arg, and only diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java index 74399cf15f..5d20ef4964 100644 --- a/src/jvm/clojure/lang/RT.java +++ b/src/jvm/clojure/lang/RT.java @@ -1244,10 +1244,7 @@ static public int intCast(float x){ } static public int intCast(long x){ - int i = (int) x; - if(i != x) - throw new IllegalArgumentException("Value out of range for int: " + x); - return i; + return Math.toIntExact(x); } static public int intCast(double x){ diff --git a/src/jvm/clojure/lang/Repeat.java b/src/jvm/clojure/lang/Repeat.java index 2ce4f2dab2..9547d66523 100644 --- a/src/jvm/clojure/lang/Repeat.java +++ b/src/jvm/clojure/lang/Repeat.java @@ -97,4 +97,17 @@ public Object reduce(IFn f, Object start){ } } +public int hashCode(){ + if(count <= 0) + throw new UnsupportedOperationException(); + else + return super.hashCode(); +} + +public int hasheq(){ + if(count <= 0) + throw new UnsupportedOperationException(); + else + return super.hasheq(); +} } diff --git a/test/clojure/test_clojure/clojure_xml.clj b/test/clojure/test_clojure/clojure_xml.clj index cf7eb9508d..d37962f361 100644 --- a/test/clojure/test_clojure/clojure_xml.clj +++ b/test/clojure/test_clojure/clojure_xml.clj @@ -11,9 +11,18 @@ (ns clojure.test-clojure.clojure-xml (:use clojure.test) - (:require [clojure.xml :as xml])) - + (:require [clojure.xml :as xml]) + (:import [java.io ByteArrayInputStream])) +(deftest CLJ-2611-avoid-XXE + (let [xml-str " + + ]> +&xxe;"] + (is (= {:tag :foo, :attrs nil, :content nil} + (with-open [input (ByteArrayInputStream. (.getBytes xml-str))] + (xml/parse input)))))) ; parse ; emit-element diff --git a/test/clojure/test_clojure/control.clj b/test/clojure/test_clojure/control.clj index 92846ad38d..f3fe436b45 100644 --- a/test/clojure/test_clojure/control.clj +++ b/test/clojure/test_clojure/control.clj @@ -421,7 +421,14 @@ :b 1 :c -2 :d 4294967296 - :d 3)) + :d 3) + (are [result input] (= result (case input + #{a} :set + :foo :keyword + a :symbol)) + :symbol 'a + :keyword :foo + :set '#{a})) (testing "test warn for hash collision" (should-print-err-message #"Performance warning, .*:\d+ - hash collision of some case test constants; if selected, those entries will be tested sequentially..*\r?\n" diff --git a/test/clojure/test_clojure/data_structures.clj b/test/clojure/test_clojure/data_structures.clj index 69baf137c6..854560466b 100644 --- a/test/clojure/test_clojure/data_structures.clj +++ b/test/clojure/test_clojure/data_structures.clj @@ -1330,3 +1330,34 @@ (is (= (hash (->Rec 1 1)) (hash (assoc r :a 1)))) (is (= (hash (->Rec 1 1)) (hash (dissoc r2 :c)))) (is (= (hash (->Rec 1 1)) (hash (dissoc (assoc r :c 1) :c)))))) + +(deftest singleton-map-in-destructure-context + (let [sample-map {:a 1 :b 2} + {:keys [a] :as m1} (list sample-map)] + (is (= m1 sample-map)) + (is (= a 1)))) + +(deftest trailing-map-destructuring + (let [sample-map {:a 1 :b 2} + add (fn [& {:keys [a b]}] (+ a b)) + addn (fn [n & {:keys [a b]}] (+ n a b))] + (testing "that kwargs are applied properly given a map in place of the key/val pairs" + (is (= 3 (add :a 1 :b 2))) + (is (= 3 (add {:a 1 :b 2}))) + (is (= 13 (addn 10 :a 1 :b 2))) + (is (= 13 (addn 10 {:a 1 :b 2}))) + (is (= 103 ((partial addn 100) :a 1 {:b 2}))) + (is (= 103 ((partial addn 100 :a 1) {:b 2}))) + (is (= 107 ((partial addn 100 :a 1) {:a 5 :b 2})))) + (testing "built maps" + (let [{:as m1} (list :a 1 :b 2) + {:as m2} (list :a 1 :b 2 {:c 3}) + {:as m3} (list :a 1 :b 2 {:a 0}) + {:keys [a4] :as m4} (list nil)] + (= m1 {:a 1 :b 2}) + (= m2 {:a 1 :b 2 :c 3}) + (= m3 {:a 0 :b 2}) + (= m1 (seq-to-map-for-destructuring (list :a 1 :b 2))) + (= m2 (seq-to-map-for-destructuring (list :a 1 :b 2 {:c 3}))) + (= m3 (seq-to-map-for-destructuring (list :a 1 :b 2 {:a 0}))) + (= a4 nil))))) diff --git a/test/clojure/test_clojure/java_interop.clj b/test/clojure/test_clojure/java_interop.clj index 30fab56e56..4925284cc2 100644 --- a/test/clojure/test_clojure/java_interop.clj +++ b/test/clojure/test_clojure/java_interop.clj @@ -11,9 +11,13 @@ (ns clojure.test-clojure.java-interop (:use clojure.test) - (:require [clojure.inspector] - [clojure.set :as set]) - (:import java.util.Base64)) + (:require [clojure.data :as data] + [clojure.inspector] + [clojure.pprint :as pp] + [clojure.set :as set] + [clojure.test-clojure.proxy.examples :as proxy-examples]) + (:import java.util.Base64 + (java.util.concurrent.atomic AtomicLong AtomicInteger))) ; http://clojure.org/java_interop ; http://clojure.org/compilation @@ -175,6 +179,37 @@ str) "chain chain chain"))) +;https://clojure.atlassian.net/browse/CLJ-1973 +(deftest test-proxy-method-order + (let [class-reader (clojure.asm.ClassReader. proxy-examples/proxy1-class-name) + method-order (atom []) + method-visitor (proxy [clojure.asm.ClassVisitor] [clojure.asm.Opcodes/ASM4 nil] + (visitMethod [access name descriptor signature exceptions] + (swap! method-order conj {:name name :descriptor descriptor}) + nil)) + _ (.accept class-reader method-visitor 0) + expected [{:name "", :descriptor "()V"} + {:name "__initClojureFnMappings", :descriptor "(Lclojure/lang/IPersistentMap;)V"} + {:name "__updateClojureFnMappings", :descriptor "(Lclojure/lang/IPersistentMap;)V"} + {:name "__getClojureFnMappings", :descriptor "()Lclojure/lang/IPersistentMap;"} + {:name "clone", :descriptor "()Ljava/lang/Object;"} + {:name "hashCode", :descriptor "()I"} + {:name "toString", :descriptor "()Ljava/lang/String;"} + {:name "equals", :descriptor "(Ljava/lang/Object;)Z"} + {:name "a", :descriptor "(Ljava/io/File;)Z"} + {:name "a", :descriptor "(Ljava/lang/Boolean;)Ljava/lang/Object;"} + {:name "a", :descriptor "(Ljava/lang/Runnable;)Z"} + {:name "a", :descriptor "(Ljava/lang/String;)I"} + {:name "b", :descriptor "(Ljava/lang/String;)Ljava/lang/Object;"} + {:name "c", :descriptor "(Ljava/lang/String;)Ljava/lang/Object;"} + {:name "d", :descriptor "(Ljava/lang/String;)Ljava/lang/Object;"} + {:name "a", :descriptor "(Ljava/lang/Boolean;Ljava/lang/String;)I"} + {:name "a", :descriptor "(Ljava/lang/String;Ljava/io/File;)Z"} + {:name "a", :descriptor "(Ljava/lang/String;Ljava/lang/Runnable;)Z"} + {:name "a", :descriptor "(Ljava/lang/String;Ljava/lang/String;)I"}] + actual @method-order] + (is (= expected actual) + (with-out-str (pp/pprint (data/diff expected actual)))))) ;; serialized-proxy can be regenerated using a modified version of ;; Clojure with the proxy serialization prohibition disabled and the @@ -589,3 +624,18 @@ (is (= (char \a) \a))) ;; Note: More coercions in numbers.clj + +; Test that primitive boxing elision in statement context works +; correctly (CLJ-2621) + +(defn inc-atomic-int [^AtomicInteger l] + (.incrementAndGet l) + nil) + +(defn inc-atomic-long [^AtomicLong l] + (.incrementAndGet l) + nil) + +(deftest test-boxing-prevention-when-compiling-statements + (is (= 1 (.get (doto (AtomicInteger. 0) inc-atomic-int)))) + (is (= 1 (.get (doto (AtomicLong. 0) inc-atomic-long))))) diff --git a/test/clojure/test_clojure/keywords.clj b/test/clojure/test_clojure/keywords.clj index 9ef86d7dd2..614fbc14e2 100644 --- a/test/clojure/test_clojure/keywords.clj +++ b/test/clojure/test_clojure/keywords.clj @@ -23,3 +23,9 @@ (are [result lookup] (= result (find-keyword this-ns lookup)) ::foo "foo" nil (str absent-keyword-sym))))) + +(deftest arity-exceptions + (is (thrown-with-msg? IllegalArgumentException #"Wrong number of args \(0\) passed to: :kw" (:kw))) + (is (thrown-with-msg? IllegalArgumentException #"Wrong number of args \(20\) passed to: :foo/bar" (apply :foo/bar (range 20)))) + (is (thrown-with-msg? IllegalArgumentException #"Wrong number of args \(21\) passed to: :foo/bar" (apply :foo/bar (range 21)))) + (is (thrown-with-msg? IllegalArgumentException #"Wrong number of args \(22\) passed to: :foo/bar" (apply :foo/bar (range 22))))) diff --git a/test/clojure/test_clojure/math.clj b/test/clojure/test_clojure/math.clj new file mode 100644 index 0000000000..4520b41b2b --- /dev/null +++ b/test/clojure/test_clojure/math.clj @@ -0,0 +1,326 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns clojure.test-clojure.math + (:require + [clojure.test :refer :all] + [clojure.math :as m])) + +(set! *warn-on-reflection* true) + +(defn neg-zero? + [^double d] + (and (zero? d) (< (Double/compare d 0.0) 0))) + +(defn pos-zero? + [^double d] + (and (zero? d) (not (< (Double/compare d 0.0) 0)))) + +(defn ulp= + "Tests that y = x +/- m*ulp(x)" + [x y ^double m] + (let [mu (* (m/ulp x) m)] + (<= (- x mu) y (+ x mu)))) + +(deftest test-sin + (is (NaN? (m/sin ##NaN))) + (is (NaN? (m/sin ##-Inf))) + (is (NaN? (m/sin ##Inf))) + (is (pos-zero? (m/sin 0.0))) + (is (neg-zero? (m/sin -0.0))) + (is (ulp= (m/sin m/PI) (- (m/sin (- m/PI))) 1))) + +(deftest test-cos + (is (NaN? (m/cos ##NaN))) + (is (NaN? (m/cos ##-Inf))) + (is (NaN? (m/cos ##Inf))) + (is (= 1.0 (m/cos 0.0) (m/cos -0.0))) + (is (ulp= (m/cos m/PI) (m/cos (- m/PI)) 1))) + +(deftest test-tan + (is (NaN? (m/tan ##NaN))) + (is (NaN? (m/tan ##-Inf))) + (is (NaN? (m/tan ##Inf))) + (is (pos-zero? (m/tan 0.0))) + (is (neg-zero? (m/tan -0.0))) + (is (ulp= (- (m/tan m/PI)) (m/tan (- m/PI)) 1))) + +(deftest test-asin + (is (NaN? (m/asin ##NaN))) + (is (NaN? (m/asin 2.0))) + (is (NaN? (m/asin -2.0))) + (is (zero? (m/asin -0.0)))) + +(deftest test-acos + (is (NaN? (m/acos ##NaN))) + (is (NaN? (m/acos -2.0))) + (is (NaN? (m/acos 2.0))) + (is (ulp= (* 2 (m/acos 0.0)) m/PI 1))) + +(deftest test-atan + (is (NaN? (m/atan ##NaN))) + (is (pos-zero? (m/atan 0.0))) + (is (neg-zero? (m/atan -0.0))) + (is (ulp= (m/atan 1) 0.7853981633974483 1))) + +(deftest test-radians-degrees-roundtrip + (doseq [d (range 0.0 360.0 5.0)] + (is (ulp= (m/round d) (m/round (-> d m/to-radians m/to-degrees)) 1)))) + +(deftest test-exp + (is (NaN? (m/exp ##NaN))) + (is (= ##Inf (m/exp ##Inf))) + (is (pos-zero? (m/exp ##-Inf))) + (is (ulp= (m/exp 0.0) 1.0 1)) + (is (ulp= (m/exp 1) m/E 1))) + +(deftest test-log + (is (NaN? (m/log ##NaN))) + (is (NaN? (m/log -1.0))) + (is (= ##Inf (m/log ##Inf))) + (is (= ##-Inf (m/log 0.0))) + (is (ulp= (m/log m/E) 1.0 1))) + +(deftest test-log10 + (is (NaN? (m/log10 ##NaN))) + (is (NaN? (m/log10 -1.0))) + (is (= ##Inf (m/log10 ##Inf))) + (is (= ##-Inf (m/log10 0.0))) + (is (ulp= (m/log10 10) 1.0 1))) + +(deftest test-sqrt + (is (NaN? (m/sqrt ##NaN))) + (is (NaN? (m/sqrt -1.0))) + (is (= ##Inf (m/sqrt ##Inf))) + (is (pos-zero? (m/sqrt 0))) + (is (= (m/sqrt 4.0) 2.0))) + +(deftest test-cbrt + (is (NaN? (m/cbrt ##NaN))) + (is (= ##-Inf (m/cbrt ##-Inf))) + (is (= ##Inf (m/cbrt ##Inf))) + (is (pos-zero? (m/cbrt 0))) + (is (= 2.0 (m/cbrt 8.0)))) + +(deftest test-IEEE-remainder + (is (NaN? (m/IEEE-remainder ##NaN 1.0))) + (is (NaN? (m/IEEE-remainder 1.0 ##NaN))) + (is (NaN? (m/IEEE-remainder ##Inf 2.0))) + (is (NaN? (m/IEEE-remainder ##-Inf 2.0))) + (is (NaN? (m/IEEE-remainder 2 0.0))) + (is (= 1.0 (m/IEEE-remainder 5.0 4.0)))) + +(deftest test-ceil + (is (NaN? (m/ceil ##NaN))) + (is (= ##Inf (m/ceil ##Inf))) + (is (= ##-Inf (m/ceil ##-Inf))) + (is (= 4.0 (m/ceil m/PI)))) + +(deftest test-floor + (is (NaN? (m/floor ##NaN))) + (is (= ##Inf (m/floor ##Inf))) + (is (= ##-Inf (m/floor ##-Inf))) + (is (= 3.0 (m/floor m/PI)))) + +(deftest test-rint + (is (NaN? (m/rint ##NaN))) + (is (= ##Inf (m/rint ##Inf))) + (is (= ##-Inf (m/rint ##-Inf))) + (is (= 1.0 (m/rint 1.2))) + (is (neg-zero? (m/rint -0.01)))) + +(deftest test-atan2 + (is (NaN? (m/atan2 ##NaN 1.0))) + (is (NaN? (m/atan2 1.0 ##NaN))) + (is (pos-zero? (m/atan2 0.0 1.0))) + (is (neg-zero? (m/atan2 -0.0 1.0))) + (is (ulp= (m/atan2 0.0 -1.0) m/PI 2)) + (is (ulp= (m/atan2 -0.0 -1.0) (- m/PI) 2)) + (is (ulp= (* 2.0 (m/atan2 1.0 0.0)) m/PI 2)) + (is (ulp= (* -2.0 (m/atan2 -1.0 0.0)) m/PI 2)) + (is (ulp= (* 4.0 (m/atan2 ##Inf ##Inf)) m/PI 2)) + (is (ulp= (/ (* 4.0 (m/atan2 ##Inf ##-Inf)) 3.0) m/PI 2)) + (is (ulp= (* -4.0 (m/atan2 ##-Inf ##Inf)) m/PI 2)) + (is (ulp= (/ (* -4.0 (m/atan2 ##-Inf ##-Inf)) 3.0) m/PI 2))) + +(deftest test-pow + (is (= 1.0 (m/pow 4.0 0.0))) + (is (= 1.0 (m/pow 4.0 -0.0))) + (is (= 4.2 (m/pow 4.2 1.0))) + (is (NaN? (m/pow 4.2 ##NaN))) + (is (NaN? (m/pow ##NaN 2.0))) + (is (= ##Inf (m/pow 2.0 ##Inf))) + (is (= ##Inf (m/pow 0.5 ##-Inf))) + (is (= 0.0 (m/pow 2.0 ##-Inf))) + (is (= 0.0 (m/pow 0.5 ##Inf))) + (is (NaN? (m/pow 1.0 ##Inf))) + (is (pos-zero? (m/pow 0.0 1.5))) + (is (pos-zero? (m/pow ##Inf -2.0))) + (is (= ##Inf (m/pow 0.0 -2.0))) + (is (= ##Inf (m/pow ##Inf 2.0))) + (is (pos-zero? (m/pow -0.0 1.5))) + (is (pos-zero? (m/pow ##-Inf -1.5))) + (is (neg-zero? (m/pow -0.0 3.0))) + (is (neg-zero? (m/pow ##-Inf -3.0))) + (is (= ##Inf (m/pow -0.0 -1.5))) + (is (= ##Inf (m/pow ##-Inf 2.5))) + (is (= ##-Inf (m/pow -0.0 -3.0))) + (is (= ##-Inf (m/pow ##-Inf 3.0))) + (is (= 4.0 (m/pow -2.0 2.0))) + (is (= -8.0 (m/pow -2.0 3.0))) + (is (= 8.0 (m/pow 2.0 3.0)))) + +(deftest test-round + (is (= 0 (m/round ##NaN))) + (is (= Long/MIN_VALUE (m/round ##-Inf))) + (is (= Long/MIN_VALUE (m/round (- Long/MIN_VALUE 2.0)))) + (is (= Long/MAX_VALUE (m/round ##Inf))) + (is (= Long/MAX_VALUE (m/round (+ Long/MAX_VALUE 2.0)))) + (is (= 4 (m/round 3.5)))) + +(deftest test-add-exact + (try + (m/add-exact Long/MAX_VALUE 1) + (is false) + (catch ArithmeticException _ + (is true)))) + +(deftest test-subtract-exact + (try + (m/subtract-exact Long/MIN_VALUE 1) + (is false) + (catch ArithmeticException _ + (is true)))) + +(deftest test-multiply-exact + (try + (m/multiply-exact Long/MAX_VALUE 2) + (is false) + (catch ArithmeticException _ + (is true)))) + +(deftest test-increment-exact + (try + (m/increment-exact Long/MAX_VALUE) + (is false) + (catch ArithmeticException _ + (is true)))) + +(deftest test-decrement-exact + (try + (m/decrement-exact Long/MIN_VALUE) + (is false) + (catch ArithmeticException _ + (is true)))) + +(deftest test-negate-exact + (is (= (inc Long/MIN_VALUE) (m/negate-exact Long/MAX_VALUE))) + (try + (m/negate-exact Long/MIN_VALUE) + (is false) + (catch ArithmeticException _ + (is true)))) + +(deftest test-floor-div + (is (= Long/MIN_VALUE (m/floor-div Long/MIN_VALUE -1))) + (is (= -1 (m/floor-div -2 5)))) + +(deftest test-floor-mod + (is (= 3 (m/floor-mod -2 5)))) + +(deftest test-ulp + (is (NaN? (m/ulp ##NaN))) + (is (= ##Inf (m/ulp ##Inf))) + (is (= ##Inf (m/ulp ##-Inf))) + (is (= Double/MIN_VALUE (m/ulp 0.0))) + (is (= (m/pow 2 971) (m/ulp Double/MAX_VALUE))) + (is (= (m/pow 2 971) (m/ulp (- Double/MAX_VALUE))))) + +(deftest test-signum + (is (NaN? (m/signum ##NaN))) + (is (zero? (m/signum 0.0))) + (is (zero? (m/signum -0.0))) + (is (= 1.0 (m/signum 42.0))) + (is (= -1.0 (m/signum -42.0)))) + +(deftest test-sinh + (is (NaN? (m/sinh ##NaN))) + (is (= ##Inf (m/sinh ##Inf))) + (is (= ##-Inf (m/sinh ##-Inf))) + (is (= 0.0 (m/sinh 0.0)))) + +(deftest test-cosh + (is (NaN? (m/cosh ##NaN))) + (is (= ##Inf (m/cosh ##Inf))) + (is (= ##Inf (m/cosh ##-Inf))) + (is (= 1.0 (m/cosh 0.0)))) + +(deftest test-tanh + (is (NaN? (m/tanh ##NaN))) + (is (= 1.0 (m/tanh ##Inf))) + (is (= -1.0 (m/tanh ##-Inf))) + (is (= 0.0 (m/tanh 0.0)))) + +(deftest test-hypot + (is (= ##Inf (m/hypot 1.0 ##Inf))) + (is (= ##Inf (m/hypot ##Inf 1.0))) + (is (NaN? (m/hypot ##NaN 1.0))) + (is (NaN? (m/hypot 1.0 ##NaN))) + (is (= 13.0 (m/hypot 5.0 12.0)))) + +(deftest test-expm1 + (is (NaN? (m/expm1 ##NaN))) + (is (= ##Inf (m/expm1 ##Inf))) + (is (= -1.0 (m/expm1 ##-Inf))) + (is (= 0.0 (m/expm1 0.0)))) + +(deftest test-log1p + (is (NaN? (m/log1p ##NaN))) + (is (= ##Inf (m/log1p ##Inf))) + (is (= ##-Inf (m/log1p -1.0))) + (is (pos-zero? (m/log1p 0.0))) + (is (neg-zero? (m/log1p -0.0)))) + +(deftest test-copy-sign + (is (= 1.0 (m/copy-sign 1.0 42.0))) + (is (= -1.0 (m/copy-sign 1.0 -42.0))) + (is (= -1.0 (m/copy-sign 1.0 ##-Inf)))) + +(deftest test-get-exponent + (is (= (inc Double/MAX_EXPONENT) (m/get-exponent ##NaN))) + (is (= (inc Double/MAX_EXPONENT) (m/get-exponent ##Inf))) + (is (= (inc Double/MAX_EXPONENT) (m/get-exponent ##-Inf))) + (is (= (dec Double/MIN_EXPONENT) (m/get-exponent 0.0))) + (is (= 0 (m/get-exponent 1.0))) + (is (= 13 (m/get-exponent 12345.678)))) + +(deftest test-next-after + (is (NaN? (m/next-after ##NaN 1))) + (is (NaN? (m/next-after 1 ##NaN))) + (is (pos-zero? (m/next-after 0.0 0.0))) + (is (neg-zero? (m/next-after -0.0 -0.0))) + (is (= Double/MAX_VALUE (m/next-after ##Inf 1.0))) + (is (pos-zero? (m/next-after Double/MIN_VALUE -1.0)))) + +(deftest test-next-up + (is (NaN? (m/next-up ##NaN))) + (is (= ##Inf (m/next-up ##Inf))) + (is (= Double/MIN_VALUE (m/next-up 0.0)))) + +(deftest test-next-down + (is (NaN? (m/next-down ##NaN))) + (is (= ##-Inf (m/next-down ##-Inf))) + (is (= (- Double/MIN_VALUE) (m/next-down 0.0)))) + +(deftest test-scalb + (is (NaN? (m/scalb ##NaN 1))) + (is (= ##Inf (m/scalb ##Inf 1))) + (is (= ##-Inf (m/scalb ##-Inf 1))) + (is (pos-zero? (m/scalb 0.0 2))) + (is (neg-zero? (m/scalb -0.0 2))) + (is (= 32.0 (m/scalb 2.0 4)))) diff --git a/test/clojure/test_clojure/multimethods.clj b/test/clojure/test_clojure/multimethods.clj index 924b0bcbeb..435f0f64a5 100644 --- a/test/clojure/test_clojure/multimethods.clj +++ b/test/clojure/test_clojure/multimethods.clj @@ -203,6 +203,43 @@ (testing "The prefers method now returns the correct table" (is (= {[::rect ::shape] #{[::shape ::rect]}} (prefers bar))))) +(deftest indirect-preferences-mulitmethod-test + (testing "Using global hierarchy" + (derive ::parent-1 ::grandparent-1) + (derive ::parent-2 ::grandparent-2) + (derive ::child ::parent-1) + (derive ::child ::parent-2) + (testing "x should be preferred over y if x is preferred over an ancestor of y" + (defmulti indirect-1 keyword) + (prefer-method indirect-1 ::parent-1 ::grandparent-2) + (defmethod indirect-1 ::parent-1 [_] ::parent-1) + (defmethod indirect-1 ::parent-2 [_] ::parent-2) + (is (= ::parent-1 (indirect-1 ::child)))) + (testing "x should be preferred over y if an ancestor of x is preferred over y" + (defmulti indirect-2 keyword) + (prefer-method indirect-2 ::grandparent-1 ::parent-2) + (defmethod indirect-2 ::parent-1 [_] ::parent-1) + (defmethod indirect-2 ::parent-2 [_] ::parent-2) + (is (= ::parent-1 (indirect-2 ::child))))) + (testing "Using custom hierarchy" + (def local-h (-> (make-hierarchy) + (derive :parent-1 :grandparent-1) + (derive :parent-2 :grandparent-2) + (derive :child :parent-1) + (derive :child :parent-2))) + (testing "x should be preferred over y if x is preferred over an ancestor of y" + (defmulti indirect-3 keyword :hierarchy #'local-h) + (prefer-method indirect-3 :parent-1 :grandparent-2) + (defmethod indirect-3 :parent-1 [_] :parent-1) + (defmethod indirect-3 :parent-2 [_] :parent-2) + (is (= :parent-1 (indirect-3 :child)))) + (testing "x should be preferred over y if an ancestor of x is preferred over y" + (defmulti indirect-4 keyword :hierarchy #'local-h) + (prefer-method indirect-4 :grandparent-1 :parent-2) + (defmethod indirect-4 :parent-1 [_] :parent-1) + (defmethod indirect-4 :parent-2 [_] :parent-2) + (is (= :parent-1 (indirect-4 :child)))))) + (deftest remove-all-methods-test (testing "Core function remove-all-methods works" (defmulti simple1 identity) diff --git a/test/clojure/test_clojure/ns_libs.clj b/test/clojure/test_clojure/ns_libs.clj index 0e470d7394..256f99ae87 100644 --- a/test/clojure/test_clojure/ns_libs.clj +++ b/test/clojure/test_clojure/ns_libs.clj @@ -103,3 +103,43 @@ (is (thrown-with-cause-msg? clojure.lang.Compiler$CompilerException #"defrecord and deftype fields must be symbols, user\.MyType had: :key1" (eval '(deftype MyType [:key1]))))) + +(deftest require-as-alias + ;; :as-alias does not load + (require '[not.a.real.ns [foo :as-alias foo] + [bar :as-alias bar]]) + (let [aliases (ns-aliases *ns*) + foo-ns (get aliases 'foo) + bar-ns (get aliases 'bar)] + (is (= 'not.a.real.ns.foo (ns-name foo-ns))) + (is (= 'not.a.real.ns.bar (ns-name bar-ns)))) + + (is (= :not.a.real.ns.foo/baz (read-string "::foo/baz"))) + + ;; can use :as-alias in use, but load will occur + (use '[clojure.walk :as-alias e1]) + (is (= 'clojure.walk (ns-name (get (ns-aliases *ns*) 'e1)))) + (is (= :clojure.walk/walk (read-string "::e1/walk"))) + + ;; can use both :as and :as-alias + (require '[clojure.set :as n1 :as-alias n2]) + (let [aliases (ns-aliases *ns*)] + (is (= 'clojure.set (ns-name (get aliases 'n1)))) + (is (= 'clojure.set (ns-name (get aliases 'n2)))) + (is (= (resolve 'n1/union) #'clojure.set/union)) + (is (= (resolve 'n2/union) #'clojure.set/union)))) + +(deftest require-as-alias-then-load-later + ;; alias but don't load + (require '[clojure.test-clojure.ns-libs-load-later :as-alias alias-now]) + (is (contains? (ns-aliases *ns*) 'alias-now)) + (is (not (nil? (find-ns 'clojure.test-clojure.ns-libs-load-later)))) + + ;; not loaded! + (is (nil? (resolve 'alias-now/example))) + + ;; load + (require 'clojure.test-clojure.ns-libs-load-later) + + ;; now loaded! + (is (not (nil? (resolve 'alias-now/example))))) \ No newline at end of file diff --git a/test/clojure/test_clojure/ns_libs_load_later.clj b/test/clojure/test_clojure/ns_libs_load_later.clj new file mode 100644 index 0000000000..2a45b86ab9 --- /dev/null +++ b/test/clojure/test_clojure/ns_libs_load_later.clj @@ -0,0 +1,12 @@ +; 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. + +;; used by clojure.test-clojure.ns-libs/require-as-alias-then-load-later +(ns clojure.test-clojure.ns-libs-load-later) + +(defn example [] true) diff --git a/test/clojure/test_clojure/numbers.clj b/test/clojure/test_clojure/numbers.clj index 26140a7c29..bf822cc947 100644 --- a/test/clojure/test_clojure/numbers.clj +++ b/test/clojure/test_clojure/numbers.clj @@ -181,9 +181,12 @@ (let [wrapped (fn [x] (try (f x) - (catch IllegalArgumentException e :error)))] + (catch RuntimeException e :error)))] (is (= vals (map wrapped inputs))))))) +(deftest test-prim-with-matching-hint + (is (= 1 (let [x 1.2] (Math/round ^double x))))) + ;; *** Functions *** (defonce DELTA 1e-12) @@ -644,6 +647,20 @@ Math/pow overflows to Infinity." (is (= java.lang.Long (class (min 1.0 2.0 -10)))) (is (= java.lang.Double (class (min 1 2 -10.0 3 4 5)))))) +(deftest test-abs + (are [in ex] (= ex (abs in)) + -1 1 + 1 1 + Long/MIN_VALUE Long/MIN_VALUE ;; special case! + -1.0 1.0 + -0.0 0.0 + ##-Inf ##Inf + ##Inf ##Inf + -123.456M 123.456M + -123N 123N + -1/5 1/5) + (is (NaN? (abs ##NaN)))) + (deftest clj-868 (testing "min/max: NaN is contagious" (letfn [(fnan? [^Float x] (Float/isNaN x)) diff --git a/test/clojure/test_clojure/other_functions.clj b/test/clojure/test_clojure/other_functions.clj index 0f3093d183..517f633fcc 100644 --- a/test/clojure/test_clojure/other_functions.clj +++ b/test/clojure/test_clojure/other_functions.clj @@ -277,7 +277,9 @@ ((some-fn number? odd? #(> % 0)) 2 4 6 8 -10) ;; 3 preds, short-circuiting ((some-fn number? odd? #(> % 0)) 1 :a) + ((some-fn number? odd? #(> % 0)) :a 1) ((some-fn number? odd? #(> % 0)) 1 3 :a) + ((some-fn number? odd? #(> % 0)) :a 1 3) ((some-fn number? odd? #(> % 0)) 1 3 5 :a) ((some-fn number? odd? #(> % 0)) 1 :a 3 5 7) ;; 4 preds @@ -379,3 +381,21 @@ ;; rest arity {:a 5} (update {:a 1} :a + 1 1 1 1) {:a 6} (update {:a 1} :a + 1 1 1 1 1))) + +(deftest test-update-vals + (let [inm (with-meta {:a 1 :b 2} {:has :meta})] + (are [result expr] (= result expr) + {:a 2 :b 3} (update-vals inm inc) + {:has :meta} (meta (update-vals inm inc)) + {0 2 2 4} (update-vals (hash-map 0 1 2 3) inc) + {0 2 2 4} (update-vals (array-map 0 1 2 3) inc) + {0 2 2 4} (update-vals (sorted-map 2 3 0 1) inc)))) + +(deftest test-update-keys + (let [inm (with-meta {:a 1 :b 2} {:has :meta})] + (are [result expr] (= result expr) + {"a" 1 "b" 2} (update-keys inm name) + {:has :meta} (meta (update-keys inm name)) + {1 1 3 3} (update-keys (hash-map 0 1 2 3) inc) + {1 1 3 3} (update-keys (array-map 0 1 2 3) inc) + {1 1 3 3} (update-keys (sorted-map 2 3 0 1) inc)))) diff --git a/test/clojure/test_clojure/parse.clj b/test/clojure/test_clojure/parse.clj new file mode 100644 index 0000000000..8078c527f6 --- /dev/null +++ b/test/clojure/test_clojure/parse.clj @@ -0,0 +1,102 @@ +(ns clojure.test-clojure.parse + (:require + [clojure.test :refer :all] + [clojure.test.check :as chk] + [clojure.test.check.generators :as gen] + [clojure.test.check.properties :as prop]) + (:import + [java.util UUID])) + +(deftest test-parse-long + (are [s expected] + (= expected (parse-long s)) + "100" 100 + "+100" 100 + "0" 0 + "+0" 0 + "-0" 0 + "-42" -42 + "9223372036854775807" Long/MAX_VALUE + "+9223372036854775807" Long/MAX_VALUE + "-9223372036854775808" Long/MIN_VALUE + "077" 77) ;; leading 0s are ignored! (not octal) + + (are [s] ;; do not parse + (nil? (parse-long s)) + "0.3" ;; no float + "9223372036854775808" ;; past max long + "-9223372036854775809" ;; past min long + "0xA0" ;; no hex + "2r010")) ;; no radix support + +;; generative test - gen long -> str -> parse, compare +(deftest test-gen-parse-long + (let [res (chk/quick-check + 100000 + (prop/for-all* [gen/large-integer] + #(= % (-> % str parse-long))))] + (if (:result res) + (is true) ;; pass + (is (:result res) (pr-str res))))) + +(deftest test-parse-double + (are [s expected] + (= expected (parse-double s)) + "1.234" 1.234 + "+1.234" 1.234 + "-1.234" -1.234 + "+0" +0.0 + "-0.0" -0.0 + "0.0" 0.0 + "5" 5.0 + "Infinity" Double/POSITIVE_INFINITY + "-Infinity" Double/NEGATIVE_INFINITY + "1.7976931348623157E308" Double/MAX_VALUE + "4.9E-324" Double/MIN_VALUE + "1.7976931348623157E309" Double/POSITIVE_INFINITY ;; past max double + "2.5e-324" Double/MIN_VALUE ;; past min double, above half minimum + "2.4e-324" 0.0) ;; below minimum double + (is (Double/isNaN (parse-double "NaN"))) + (are [s] ;; nil on invalid string + (nil? (parse-double s)) + "double" ;; invalid string + "1.7976931348623157G309")) ;; invalid, but similar to valid + +;; generative test - gen double -> str -> parse, compare +(deftest test-gen-parse-double + (let [res (chk/quick-check + 100000 + (prop/for-all* [gen/double] + #(let [parsed (-> % str parse-double)] + (if (Double/isNaN %) + (Double/isNaN parsed) + (= % parsed)))))] + (if (:result res) + (is true) ;; pass + (is (:result res) (pr-str res))))) + +(deftest test-parse-uuid + (is (parse-uuid (.toString (UUID/randomUUID)))) + (is (nil? (parse-uuid "BOGUS"))) ;; nil on invalid uuid string + (are [s] ;; throw on invalid type (not string) + (try (parse-uuid s) (is false) (catch Throwable _ (is true))) + 123 + nil)) + +(deftest test-parse-boolean + (is (identical? true (parse-boolean "true"))) + (is (identical? false (parse-boolean "false"))) + + (are [s] ;; nil on invalid string + (nil? (parse-boolean s)) + "abc" + "TRUE" + "FALSE" + " true ") + + (are [s] ;; throw on invalid type (not string) + (try (parse-boolean s) (is false) (catch Throwable _ (is true))) + nil + false + true + 100)) diff --git a/test/clojure/test_clojure/predicates.clj b/test/clojure/test_clojure/predicates.clj index 7efdc6fe71..bb9074fb5f 100644 --- a/test/clojure/test_clojure/predicates.clj +++ b/test/clojure/test_clojure/predicates.clj @@ -172,3 +172,23 @@ (dotimes [i (count row)] (is (= ((resolve (nth preds i)) v) (nth row i)) (pr-str (list (nth preds i) v)))))))) + +;; Special double predicates + +(deftest test-double-preds + (is (NaN? ##NaN)) + (is (NaN? (Double/parseDouble "NaN"))) + (is (NaN? (Float/parseFloat "NaN"))) + (is (NaN? Float/NaN)) + (is (not (NaN? 5))) + (is (thrown? Throwable (NaN? nil))) + (is (thrown? Throwable (NaN? :xyz))) + + (is (infinite? ##Inf)) + (is (infinite? ##-Inf)) + (is (infinite? Double/POSITIVE_INFINITY)) + (is (infinite? Double/NEGATIVE_INFINITY)) + (is (infinite? Float/POSITIVE_INFINITY)) + (is (infinite? Float/NEGATIVE_INFINITY)) + (is (thrown? Throwable (infinite? nil))) + (is (thrown? Throwable (infinite? :xyz)))) \ No newline at end of file diff --git a/test/clojure/test_clojure/protocols.clj b/test/clojure/test_clojure/protocols.clj index 3072915481..4e951449bb 100644 --- a/test/clojure/test_clojure/protocols.clj +++ b/test/clojure/test_clojure/protocols.clj @@ -47,13 +47,13 @@ (deftest protocols-test (testing "protocol fns have useful metadata" (let [common-meta {:ns (find-ns 'clojure.test-clojure.protocols.examples) - :protocol #'ExampleProtocol}] - (are [m f] (= (merge (quote m) common-meta) + :protocol #'ExampleProtocol :tag nil}] + (are [m f] (= (merge common-meta m) (meta (var f))) - {:name foo :arglists ([a]) :doc "method with one arg"} foo - {:name bar :arglists ([a b]) :doc "method with two args"} bar - {:name baz :arglists ([a] [a b]) :doc "method with multiple arities" :tag String} baz - {:name with-quux :arglists ([a]) :doc "method name with a hyphen"} with-quux))) + {:name 'foo :arglists '([a]) :doc "method with one arg"} foo + {:name 'bar :arglists '([a b]) :doc "method with two args"} bar + {:name 'baz :arglists '([a] [a b]) :doc "method with multiple arities" :tag 'java.lang.String} baz + {:name 'with-quux :arglists '([a]) :doc "method name with a hyphen"} with-quux))) (testing "protocol fns throw IllegalArgumentException if no impl matches" (is (thrown-with-msg? IllegalArgumentException @@ -674,3 +674,34 @@ (deftest test-leading-dashes (is (= 10 (-do-dashed (Dashed.)))) (is (= [10] (map -do-dashed [(Dashed.)])))) + +;; see CLJ-1879 + +(deftest test-base-reduce-kv + (is (= {1 :a 2 :b} + (reduce-kv #(assoc %1 %3 %2) + {} + (seq {:a 1 :b 2}))))) + +(defn aget-long-hinted ^long [x] (aget (longs-hinted x) 0)) + +(deftest test-longs-hinted-proto + (is (= 1 + (aget-long-hinted + (reify LongsHintedProto + (longs-hinted [_] (long-array [1]))))))) + +;; CLJ-1180 - resolve type hints in protocol methods + +(import 'clojure.lang.ISeq) +(defprotocol P + (^ISeq f [_])) +(ns clojure.test-clojure.protocols.other + (:use clojure.test)) +(defn cf [val] + (let [aseq (clojure.test-clojure.protocols/f val)] + (count aseq))) +(extend-protocol clojure.test-clojure.protocols/P String + (f [s] (seq s))) +(deftest test-resolve-type-hints-in-protocol-methods + (is (= 4 (clojure.test-clojure.protocols/f "test")))) diff --git a/test/clojure/test_clojure/protocols/examples.clj b/test/clojure/test_clojure/protocols/examples.clj index 9d962d5651..0c247eab04 100644 --- a/test/clojure/test_clojure/protocols/examples.clj +++ b/test/clojure/test_clojure/protocols/examples.clj @@ -17,3 +17,5 @@ (hinted [^int i]) (hinted [^String s])) +(defprotocol LongsHintedProto + (^longs longs-hinted [_])) diff --git a/test/clojure/test_clojure/proxy/examples.clj b/test/clojure/test_clojure/proxy/examples.clj new file mode 100644 index 0000000000..b98042320f --- /dev/null +++ b/test/clojure/test_clojure/proxy/examples.clj @@ -0,0 +1,30 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns ^{:doc "Test proxy classes that are AOT-compiled for the tests in + clojure.test-clojure.java-interop." + :author "Ambrose Bonnaire-Sergeant"} + clojure.test-clojure.proxy.examples) + +(definterface A + (^int a [^String x]) + (^boolean a [^java.io.File x]) + (^boolean a [^Runnable x]) + (a [^Boolean x]) + (^int a [^Boolean x ^String y]) + (^int a [^String x ^String y]) + (^boolean a [^String x ^java.io.File y]) + (^boolean a [^String x ^Runnable y]) + (b [^String x]) + (c [^String x]) + (d [^String x])) + +(def ^String proxy1-class-name + (-> (proxy [A] []) + class + .getName)) diff --git a/test/clojure/test_clojure/run_single_test.clj b/test/clojure/test_clojure/run_single_test.clj new file mode 100644 index 0000000000..abf1084c0a --- /dev/null +++ b/test/clojure/test_clojure/run_single_test.clj @@ -0,0 +1,33 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns clojure.test-clojure.run-single-test + (:require [clojure.test :refer [is deftest run-test run-tests]] + [clojure.test-helper :refer [with-err-string-writer]] + [clojure.test-clojure.test-fixtures :as tf])) + +(defn not-a-test + []) + +(defmacro should-print-to-err + [re & body] + `(is (re-find ~re (with-err-string-writer ~@body)))) + +(deftest reports-missing-var + (should-print-to-err #"^Unable to resolve .*/function-missing to a test function.*" + (let [result (eval `(run-test function-missing))] + (is (nil? result))))) + +(deftest reports-non-test-var + (should-print-to-err #"^.*/not-a-test is not a test.*" + (let [result (eval `(run-test not-a-test))] + (is (nil? result))))) + +(deftest can-run-test-with-fixtures + (is (= {:test 1, :pass 2, :fail 0, :error 0, :type :summary} + (run-test tf/can-use-once-fixtures)))) diff --git a/test/clojure/test_clojure/sequences.clj b/test/clojure/test_clojure/sequences.clj index 54d640f72d..f0927061a4 100644 --- a/test/clojure/test_clojure/sequences.clj +++ b/test/clojure/test_clojure/sequences.clj @@ -988,7 +988,7 @@ {} {:a 1 :b 2} #{} #{1 2} )) -(defspec longrange-equals-range 100 +(defspec longrange-equals-range 1000 (prop/for-all [start gen/int end gen/int step gen/s-pos-int] @@ -1384,3 +1384,98 @@ (when (reversible? coll) (is (= true (instance? clojure.lang.IMeta (rseq coll)))) (is (= {:a true} (meta (with-meta (rseq coll) {:a true}))))))) + +(deftest test-iteration-opts + (let [genstep (fn [steps] + (fn [k] (swap! steps inc) (inc k))) + test (fn [expect & iteropts] + (is (= expect + (let [nsteps (atom 0) + iter (apply iteration (genstep nsteps) iteropts) + ret (doall (seq iter))] + {:ret ret :steps @nsteps}) + (let [nsteps (atom 0) + iter (apply iteration (genstep nsteps) iteropts) + ret (into [] iter)] + {:ret ret :steps @nsteps}))))] + (test {:ret [1 2 3 4] + :steps 5} + :initk 0 :somef #(< % 5)) + (test {:ret [1 2 3 4 5] + :steps 5} + :initk 0 :kf (fn [ret] (when (< ret 5) ret))) + (test {:ret ["1"] + :steps 2} + :initk 0 :somef #(< % 2) :vf str)) + + ;; kf does not stop on false + (let [iter #(iteration (fn [k] + (if (boolean? k) + [10 :boolean] + [k k])) + :vf second + :kf (fn [[k v]] + (cond + (= k 3) false + (< k 14) (inc k))) + :initk 0)] + (is (= [0 1 2 3 :boolean 11 12 13 14] + (into [] (iter)) + (seq (iter)))))) + +(deftest test-iteration + ;; equivalence to line-seq + (let [readme #(java.nio.file.Files/newBufferedReader (.toPath (java.io.File. "readme.txt")))] + (is (= (with-open [r (readme)] + (vec (iteration (fn [_] (.readLine r))))) + (with-open [r (readme)] + (doall (line-seq r)))))) + + ;; paginated API + (let [items 12 pgsize 5 + src (vec (repeatedly items #(java.util.UUID/randomUUID))) + api (fn [tok] + (let [tok (or tok 0)] + (when (< tok items) + {:tok (+ tok pgsize) + :ret (subvec src tok (min (+ tok pgsize) items))})))] + (is (= src + (mapcat identity (iteration api :kf :tok :vf :ret)) + (into [] cat (iteration api :kf :tok :vf :ret))))) + + (let [src [:a :b :c :d :e] + api (fn [k] + (let [k (or k 0)] + (if (< k (count src)) + {:item (nth src k) + :k (inc k)})))] + (is (= [:a :b :c] + (vec (iteration api + :somef (comp #{:a :b :c} :item) + :kf :k + :vf :item)) + (vec (iteration api + :kf #(some-> % :k #{0 1 2}) + :vf :item)))))) + +(deftest infinite-seq-hash + (are [e] (thrown? Exception (.hashCode ^Object e)) + (iterate identity nil) + (cycle [1]) + (repeat 1)) + (are [e] (thrown? Exception (.hasheq ^clojure.lang.IHashEq e)) + (iterate identity nil) + (cycle [1]) + (repeat 1))) + +(defspec iteration-seq-equals-reduce 1000 + (prop/for-all [initk gen/int + seed gen/int] + (let [src (fn [] + (let [rng (java.util.Random. seed)] + (iteration #(unchecked-add % (.nextLong rng)) + :somef (complement #(zero? (mod % 1000))) + :vf str + :initk initk)))] + (= (into [] (src)) + (into [] (seq (src))))))) diff --git a/test/clojure/test_clojure/serialization.clj b/test/clojure/test_clojure/serialization.clj index 51a0d6a263..c9befc49cd 100644 --- a/test/clojure/test_clojure/serialization.clj +++ b/test/clojure/test_clojure/serialization.clj @@ -183,4 +183,11 @@ ;; stateful seqs (enumeration-seq (java.util.Collections/enumeration (range 50))) - (iterator-seq (.iterator (range 50))))) \ No newline at end of file + (iterator-seq (.iterator (range 50))))) + +;; necessary for CVE-2024-22871 +(deftest CLJ-2839 + (are [e] (thrown? Exception (.hashCode ^Object (-> e serialize deserialize))) + (repeat 1) + (iterate identity nil) + (cycle [1]))) \ No newline at end of file diff --git a/test/clojure/test_clojure/transducers.clj b/test/clojure/test_clojure/transducers.clj index 76a0e978a3..b7a9c665e2 100644 --- a/test/clojure/test_clojure/transducers.clj +++ b/test/clojure/test_clojure/transducers.clj @@ -398,3 +398,13 @@ (sequence (map-indexed vector) []))) (is (= [[0 1] [1 2] [2 3] [3 4]] (sequence (map-indexed vector) (range 1 5))))) + +(deftest test-into+halt-when + (is (= :anomaly (into [] (comp (filter some?) (halt-when #{:anomaly})) + [1 2 3 :anomaly 4]))) + (is (= {:anomaly :oh-no!, + :partial-results [1 2]} + (into [] + (halt-when :anomaly #(assoc %2 :partial-results %1)) + [1 2 {:anomaly :oh-no!} 3 4])))) + diff --git a/test/clojure/test_clojure/vectors.clj b/test/clojure/test_clojure/vectors.clj index 0bea3ff4b8..ea2b8e77d5 100644 --- a/test/clojure/test_clojure/vectors.clj +++ b/test/clojure/test_clojure/vectors.clj @@ -416,3 +416,15 @@ (is (= [0 1 2 3] (vec (reify clojure.lang.IReduceInit (reduce [_ f start] (reduce f start (range 4)))))))) + +(deftest test-reduce-kv-vectors + (is (= 25 (reduce-kv + 10 [2 4 6]))) + (is (= 25 (reduce-kv + 10 (subvec [0 2 4 6] 1))))) + +(deftest test-vector-eqv-to-non-counted-types + (is (not= (range) [0 1 2])) + (is (not= [0 1 2] (range))) + (is (= [0 1 2] (take 3 (range)))) + (is (= [0 1 2] (new java.util.ArrayList [0 1 2]))) + (is (not= [1 2] (take 1 (cycle [1 2])))) + (is (= [1 2 3 nil 4 5 6 nil] (eduction cat [[1 2 3 nil] [4 5 6 nil]]))))