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/doc-build.yml b/.github/workflows/doc-build.yml new file mode 100644 index 0000000000..11ff789629 --- /dev/null +++ b/.github/workflows/doc-build.yml @@ -0,0 +1,75 @@ +name: Build API Docs + +permissions: + contents: write + +on: + workflow_dispatch: + inputs: + commit: + description: 'Commit changes to gh-pages branch' + type: boolean + required: true + default: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + + - name: Set up Java + uses: actions/setup-java@v4 + with: + java-version: 8 + distribution: 'temurin' + + - name: Set up Clojure + uses: DeLaGuardo/setup-clojure@13.1 + with: + cli: 'latest' + + - name: Cache clojure dependencies + uses: actions/cache@v4 + with: + path: | + ~/.m2/repository + ~/.gitlibs + key: cljdeps-${{ hashFiles('deps.edn') }} + restore-keys: cljdeps- + + - name: Clone clojure api doc repo + uses: actions/checkout@v4 + with: + repository: clojure/clojure-api-doc + path: clojure-api-doc + fetch-depth: 0 + + - name: Clone clojure source code repo into clojure-api-doc + uses: actions/checkout@v4 + with: + path: clojure-api-doc/repo + fetch-depth: 0 + + - name: Clone clojure gh-pages branch into clojure-api-doc + uses: actions/checkout@v4 + with: + repository: clojure/clojure + path: clojure-api-doc/repo-docs + ref: 'gh-pages' + fetch-depth: 0 + + - name: Install markdown + run: sudo apt install markdown + + - name: Call clojure-api-doc build.sh + run: bash ${GITHUB_WORKSPACE}/clojure-api-doc/build.sh + + - name: Commit + if: ${{inputs.commit}} + run: | + git config --global user.name clojure-build + git config --global user.email "clojure-build@users.noreply.github.com" + cd clojure-api-doc/repo-docs + git add -u -v + git commit -m "Autodoc commit" + git push origin gh-pages diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..286cf956d5 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release on demand + +permissions: + contents: write + +on: + workflow_dispatch: + inputs: + releaseVersion: + description: "Version to release" + required: true + snapshotVersion: + description: "Snapshot version after release" + required: true + +jobs: + call-release: + uses: clojure/build.ci/.github/workflows/release.yml@master + with: + releaseVersion: ${{ github.event.inputs.releaseVersion }} + snapshotVersion: ${{ github.event.inputs.snapshotVersion }} + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml new file mode 100644 index 0000000000..9fdad8c695 --- /dev/null +++ b/.github/workflows/snapshot.yml @@ -0,0 +1,11 @@ +name: Snapshot on demand + +permissions: + contents: read + +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..ebbb36eb2c --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,29 @@ +name: Test + +permissions: + contents: read + +on: + push: + workflow_dispatch: + +jobs: + test: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] # macOS-latest, windows-latest] + java-version: ["8", "11", "17", "21", "25"] + distribution: ["temurin", "corretto"] + profile: ["test-direct", "test-no-direct"] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - name: Set up Java + uses: actions/setup-java@v4 + 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..1464b69ed3 100644 --- a/build.xml +++ b/build.xml @@ -77,11 +77,20 @@ + + + + + + + + + @@ -101,6 +110,7 @@ + @@ -113,7 +123,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 9d041066d1..3244776da9 100644 --- a/changes.md +++ b/changes.md @@ -1,5 +1,501 @@ +# Changes to Clojure in Version 1.12.5 + +* [CLJ-2945](https://clojure.atlassian.net/browse/CLJ-2945) - reify - incorrectly transfers reader metadata to runtime object +* [CLJ-2228](https://clojure.atlassian.net/browse/CLJ-2228) - constantly - unroll to remove rest args allocation + +# Changes to Clojure in Version 1.12.4 + +* [CLJ-2924](https://clojure.atlassian.net/browse/CLJ-2924) - LazySeq - fix visibility issues with non-volatile reads + +# Changes to Clojure in Version 1.12.3 + +* [CLJ-2919](https://clojure.atlassian.net/browse/CLJ-2919) - Compiler - fix nested compilation emitting for keyword and protocol call sites + +# Changes to Clojure in Version 1.12.2 + +* [CLJ-2914](https://clojure.atlassian.net/browse/CLJ-2914) - Compiler - syntax error if qualified instance method expression is missing instance +* [CLJ-1798](https://clojure.atlassian.net/browse/CLJ-1798) - Refs - avoid creating RetryEx in LockingTransaction on every transaction +* [CLJ-2916](https://clojure.atlassian.net/browse/CLJ-2916) - LazySeq - realize before serializing and do not serialize IFn +* [CLJ-2917](https://clojure.atlassian.net/browse/CLJ-2917) - Iterate - de/serialization no longer supported, throw + +# Changes to Clojure in Version 1.12.1 + +* [CLJ-2899](https://clojure.atlassian.net/browse/CLJ-2899) - Revert change in semantics of qualified symbol in invocation position if field and method of same name +* [CLJ-2898](https://clojure.atlassian.net/browse/CLJ-2898) - Objects that are both IFn and FunctionalInterface unnecessarily get converted to FI +* [CLJ-2888](https://clojure.atlassian.net/browse/CLJ-2888) - `gen-class` - did not support new array class symbol syntax +* [CLJ-2886](https://clojure.atlassian.net/browse/CLJ-2886) - `add-libs` - send only procurer keys to tool invocation +* [CLJ-2906](https://clojure.atlassian.net/browse/CLJ-2906) - Add missing :added metadata to 1.12 functions + +# Changes to Clojure in Version 1.12.0 + +## 1 Compatibility + +### 1.1 Java 8 - Compatiblity EOL notice + +Clojure 1.12 produces Java 8 bytecode (same as Clojure 1.10 and 1.11), but this is expected to be the last release using a Java 8 baseline. Future releases will move the bytecode and minimum Java compatibility to a newer Java LTS release. + +### 1.2 Java 21 - Virtual thread pinning from user code under `synchronized` + +Clojure users want to use virtual threads on JDK 21. Prior to 1.12, Clojure lazy-seqs and delays, in order to enforce run-once behavior, ran user code under synchronized blocks, which as of JDK 21 don't yet participate in cooperative blocking. Thus if that code did e.g. blocking I/O it would pin a real thread. JDK 21 may emit warnings for this when using `-Djdk.tracePinnedThreads=full`. + +To avoid this pinning, in 1.12 `lazy-seq` and `delay` use locks instead of synchronized blocks. + +See: [CLJ-2804](https://clojure.atlassian.net/browse/CLJ-2804) + +### 1.3 Security + +Fix [CVE-2024-22871](https://nvd.nist.gov/vuln/detail/CVE-2024-22871) detailed in [GHSA-vr64-r9qj-h27f](https://github.com/advisories/GHSA-vr64-r9qj-h27f): + +* [CLJ-2839](https://clojure.atlassian.net/browse/CLJ-2839) `iterate`, `cycle`, `repeat` - infinite seqs have infinite `hashCode()` + +### 1.4 Serialization + +[CLJ-1327](https://clojure.atlassian.net/browse/CLJ-1327) explicitly sets the Java serialization identifier for the classes in Clojure that implement Java serialization. In Clojure 1.11.0 this changed for two classes unnecessarily and we reverted those changes in Clojure 1.11.1 - this completes that work for the rest of the classes. + +Clojure data types have implemented the Java serialization interfaces since Clojure 1.0. Java serialization is designed to save graphs of Java instances into a byte stream. Every class has an identifier (the serialVersionUID) that is automatically generated based on the class name, it's type hierarchy, and the serialized fields. At deserialization time, deserialization can only occur when the available class has an identifier that matches the class id recorded in the serialized bytes. + +Clojure has never provided a guarantee of serialization consistency across Clojure versions, but we do not wish to break compatibility any more than necessary and these changes will give us more control over that in the future. + +See: [CLJ-1327](https://clojure.atlassian.net/browse/CLJ-1327) + +### 1.5 Dependencies + +Updated dependencies: + +* spec.alpha dependency to 0.5.238 - [changes](https://github.com/clojure/spec.alpha/blob/master/CHANGES.md) +* core.specs.alpha dependency to 0.4.74 - [changes](https://github.com/clojure/core.specs.alpha/blob/master/CHANGES.md) + +See: [CLJ-2852](https://clojure.atlassian.net/browse/CLJ-2852) + +## 2 Features + +### 2.1 Add libraries for interactive use + +There are many development-time cases where it would be useful to add a library interactively without restarting the JVM - speculative evaluation, adding a known dependency to your project, or adding a library to accomplish a specific task. + +Clojure now provides new functions to add libraries interactively, without restarting the JVM or losing the state of your work: + +* [add-lib](https://clojure.github.io/clojure/branch-master/clojure.repl-api.html#clojure.repl.deps/add-lib) takes a lib that is not available on the classpath, and makes it available by downloading (if necessary) and adding to the classloader. Libs already on the classpath are not updated. If the coordinate is not provided, the newest Maven or git (if the library has an inferred git repo name) version or tag are used. +* [add-libs](https://clojure.github.io/clojure/branch-master/clojure.repl-api.html#clojure.repl.deps/add-libs) is like `add-lib`, but resolves a set of new libraries and versions together. +* [sync-deps](https://clojure.github.io/clojure/branch-master/clojure.repl-api.html#clojure.repl.deps/sync-deps) calls `add-libs` with any libs present in deps.edn, but not yet present on the classpath. + +These new functions are intended only for development-time interactive use at the repl - using a deps.edn is still the proper way to build and maintain production code. To this end, these functions all check that [\*repl*](https://clojure.github.io/clojure/branch-master/clojure.core-api.html#clojure.core/%2Arepl%2A) is bound to true (that flag is bound automatically by `clojure.main/repl`). In a clojure.main REPL, these new functions are automatically referred in the `user` namespace. In other repls, you may need to `(require '[clojure.repl.deps :refer :all])` before use. + +Library resolution and download are provided by [tools.deps](https://github.com/clojure/tools.deps). However, you do not want to add tools.deps and its many dependencies to your project classpath during development, and thus we have also added a new api for invoking functions out of process via the Clojure CLI. + +See: [CLJ-2761](https://clojure.atlassian.net/browse/CLJ-2761), [CLJ-2757](https://clojure.atlassian.net/browse/CLJ-2757), [CLJ-2788](https://clojure.atlassian.net/browse/CLJ-2788), [CLJ-2767](https://clojure.atlassian.net/browse/CLJ-2767), [CLJ-2769](https://clojure.atlassian.net/browse/CLJ-2769), [CLJ-2770](https://clojure.atlassian.net/browse/CLJ-2770) + +### 2.2 Invoke tool functions out of process + +There are many useful tools you can use at development time, but which are not part of your project's actual dependencies. The Clojure CLI provides explicit support for [tools](https://clojure.org/reference/clojure_cli#tools) with their own classpath, but there was not previously a way to invoke these interactively. + +Clojure now includes [clojure.tools.deps.interop/invoke-tool](https://clojure.github.io/clojure/branch-master/clojure.tools.deps.interop-api.html#clojure.tools.deps.interop/invoke-tool) to invoke a tool function out of process. The classpath for the tool is defined in deps.edn and you do not need to add the tool's dependencies to your project classpath. + +`add-lib` functionality is built using `invoke-tool` but you can also use it to build or invoke your own tools for interactive use. Find more about the function execution protocol on the [CLI reference](https://clojure.org/reference/clojure_cli#function_protocol). + +See: [CLJ-2760](https://clojure.atlassian.net/browse/CLJ-2760), [CLJ-2819](https://clojure.atlassian.net/browse/CLJ-2819) + +### 2.3 Start and control external processes + +For a long time, we've had the `clojure.java.shell` namespace, but over time Java has provided new APIs for process info, process control, and I/O redirection. This release adds a new namespace [clojure.java.process](https://clojure.github.io/clojure/branch-master/index.html#clojure.java.process) that takes advantage of these APIs and is easier to use. See: + +* [start](https://clojure.github.io/clojure/branch-master/clojure.java.process-api.html#clojure.java.process/start) - full control over streams with access to the underlying Java objects for advanced usage +* [exec](https://clojure.github.io/clojure/branch-master/clojure.java.process-api.html#clojure.java.process/exec) - covers the common case of executing an external process and returning its stdout on completion + +See: [CLJ-2759](https://clojure.atlassian.net/browse/CLJ-2759), [CLJ-2777](https://clojure.atlassian.net/browse/CLJ-2777), [CLJ-2828](https://clojure.atlassian.net/browse/CLJ-2828), [CLJ-2773](https://clojure.atlassian.net/browse/CLJ-2773), [CLJ-2776](https://clojure.atlassian.net/browse/CLJ-2776), [CLJ-2774](https://clojure.atlassian.net/browse/CLJ-2774), [CLJ-2778](https://clojure.atlassian.net/browse/CLJ-2778), [CLJ-2779](https://clojure.atlassian.net/browse/CLJ-2779), [CLJ-2865](https://clojure.atlassian.net/browse/CLJ-2865) + +### 2.4 Method values + +Clojure programmers often want to use Java methods in higher-order functions (e.g. passing a Java method to `map`). Until now, programmers have had to manually wrap methods in functions. This is verbose, and might require manual hinting for overload disambiguation, or incur incidental reflection or boxing. + +Programmers can now use [qualified methods](#25-qualified-methods---classmethod-classmethod-and-classnew) as ordinary functions in value contexts - the compiler will automatically generate the wrapping function. The compiler will generate a reflective call when a qualified method does not resolve due to overloading. Developers can supply [:param-tags](#26-param-tags-metadata) metadata on qualified methods to specify the signature of a single desired method, 'resolving' it. + +See: [CLJ-2793](https://clojure.atlassian.net/browse/CLJ-2793), [CLJ-2844](https://clojure.atlassian.net/browse/CLJ-2844), [CLJ-2835](https://clojure.atlassian.net/browse/CLJ-2835) + +### 2.5 Qualified methods - `Class/method`, `Class/.method`, and `Class/new` + +Java members inherently exist in a class. For method values we need a way to explicitly specify the class of an instance method because there is no possibility for inference. + +Qualified methods have value semantics when used in non-invocation positions: + +* `Classname/method` - value is a Clojure function that invokes a static method +* `Classname/.method` - value is a Clojure function that invokes an instance method +* `Classname/new` - value is a Clojure function that invokes a constructor + +Note: developers must use `Classname/method` and `Classname/.method` syntax to differentiate between static and instance methods. + +Qualified method invocations with [param-tags](#26-param-tags-metadata) use only the tags to resolve the method. Without param-tags they behave like the equivalent [dot syntax](https://clojure.org/reference/java_interop#_the_dot_special_form), except the qualifying class takes precedence over hints of the target object, and over its runtime type when invoked via reflection. + +Note: Static fields are values and should be referenced without parens unless they are intended as function calls, e.g `(System/out)` should be `System/out`. Future Clojure releases will treat the field's value as something invokable and invoke it. + +See: [CLJ-2844](https://clojure.atlassian.net/browse/CLJ-2844), [CLJ-2848](https://clojure.atlassian.net/browse/CLJ-2848), [CLJ-2847](https://clojure.atlassian.net/browse/CLJ-2847), [CLJ-2853](https://clojure.atlassian.net/browse/CLJ-2853), [CLJ-2867](https://clojure.atlassian.net/browse/CLJ-2867) + +### 2.6 :param-tags metadata + +When used as values, qualified methods supply only the class and method name, and thus cannot resolve overloaded methods. + +Developers can supply `:param-tags` metadata on qualified methods to specify the signature of a single desired method, 'resolving' it. The `:param-tags` metadata is a vector of zero or more tags: `[tag ...]`. A tag is any existing valid `:tag` metadata value. Each tag corresponds to a parameter in the desired signature (arity should match the number of tags). Parameters with non-overloaded types can use the placeholder `_` in lieu of the tag. When you supply :param-tags metadata on a qualified method, the metadata must allow the compiler to resolve it to a single method at compile time. + +A new metadata reader syntax `^[tag ...]` attaches `:param-tags` metadata to member symbols, just as `^tag` attaches `:tag` metadata to a symbol. + +See: [CLJ-2805](https://clojure.atlassian.net/browse/CLJ-2805) + +### 2.7 Array class syntax + +Clojure supports symbols naming classes both as a value (for class object) and as a type hint, but has not provided syntax for array classes other than strings. + +Developers can now refer to an array class using a symbol of the form `ComponentClass/#dimensions`, eg `String/2` refers to the class of a 2 dimensional array of Strings. Component classes can be fully-qualified classes, imported classes, or primitives. Array class syntax can be used as both type hints and values. + +Examples: `String/1`, `java.lang.String/1`, `long/2`. + +See: [CLJ-2807](https://clojure.atlassian.net/browse/CLJ-2807) + +### 2.8 Functional interfaces + +Java programs define "functions" with Java functional interfaces (marked with the [@FunctionalInterface](https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html) annotation), which have a single method. + +Clojure developers can now invoke Java methods taking functional interfaces by passing functions with matching arity. The Clojure compiler implicitly converts Clojure functions to the required functional interface by constructing a lambda adapter. You can explicitly coerce a Clojure function to a functional interface by hinting the binding name in a `let` binding, e.g. to avoid repeated adapter construction in a loop, e.g. `(let [^java.util.function.Predicate p even?] ...)`. + +See: [CLJ-2799](https://clojure.atlassian.net/browse/CLJ-2799), [CLJ-2858](https://clojure.atlassian.net/browse/CLJ-2858), [CLJ-2856](https://clojure.atlassian.net/browse/CLJ-2856), [CLJ-2863](https://clojure.atlassian.net/browse/CLJ-2863), [CLJ-2864](https://clojure.atlassian.net/browse/CLJ-2864) + +### 2.9 Java Supplier interop + +Calling methods that take a [Supplier](https://docs.oracle.com/javase/8/docs/api/java/util/function/Supplier.html) (a method that supplies a value) had required writing an adapter with reify. Clojure has a "value supplier" interface with semantic support already - `IDeref`. All `IDeref` impls (`delay`, `future`, `atom`, etc) now implement the `Supplier` interface directly. + +See: [CLJ-2792](https://clojure.atlassian.net/browse/CLJ-2792), [CLJ-2841](https://clojure.atlassian.net/browse/CLJ-2841) + +### 2.10 Streams with seq, into, reduce, and transduce support + +Java APIs increasingly return [Stream](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html)s and are hard to consume because they do not implement interfaces that Clojure already supports, and hard to interop with because Clojure doesn't directly implement Java functional interfaces. + +In addition to functional interface support, Clojure now provides these functions to interoperate with streams in an idiomatic manner, all functions behave analogously to their Clojure counterparts: + +* `(stream-seq! stream) => seq` +* `(stream-reduce! f [init-val] stream) => val` +* `(stream-transduce! xf f [init-val] stream) => val` +* `(stream-into! to-coll [xf] stream) => to-coll` + +All of these operations are terminal stream operations (they consume the stream). + +See: [CLJ-2775](https://clojure.atlassian.net/browse/CLJ-2775) + +### 2.11 PersistentVector implements Spliterable + +Java collections implement streams via ["spliterators"](https://docs.oracle.com/javase/8/docs/api/java/util/Spliterator.html), iterators that can be split for faster parallel traversal. `PersistentVector` now provides a custom spliterator that supports parallelism, with greatly improved performance. + +See: [CLJ-2791](https://clojure.atlassian.net/browse/CLJ-2791) + +### 2.12 Efficient drop and partition for persistent or algorithmic collections + +Partitioning of a collection uses a series of takes (to build a partition) and drops (to skip past that partition). [CLJ-2713](https://clojure.atlassian.net/browse/CLJ-2713) adds a new internal interface (IDrop) indicating that a collection can drop more efficiently than sequential traversal, and implements that for persistent collections and algorithmic collections like `range` and `repeat`. These optimizations are used in `drop`, `nthrest`, and `nthnext`. + +Additionally, there are new functions `partitionv`, `partitionv-all`, and `splitv-at` that are more efficient than their existing counterparts and produce vector partitions instead of realized seq partitions. + +See: [CLJ-2713](https://clojure.atlassian.net/browse/CLJ-2713), [CLJ-2742](https://clojure.atlassian.net/browse/CLJ-2742), [CLJ-2740](https://clojure.atlassian.net/browse/CLJ-2740), [CLJ-2715](https://clojure.atlassian.net/browse/CLJ-2715), [CLJ-2718](https://clojure.atlassian.net/browse/CLJ-2718), [CLJ-2772](https://clojure.atlassian.net/browse/CLJ-2772), [CLJ-2741](https://clojure.atlassian.net/browse/CLJ-2741) + +### 2.13 Var interning policy + +[Interning](https://clojure.org/reference/vars#interning) a var in a namespace (vs aliasing) must create a stable reference that is never displaced, so that all references to an interned var get the same object. There were some cases where interned vars could get displaced and those have been tightened up in 1.12.0-alpha1. If you encounter this situation, you'll see a warning like "REJECTED: attempt to replace interned var #'some-ns/foo with #'other-ns/foo in some-ns, you must ns-unmap first". + +This addresses the root cause of an issue encountered with Clojure 1.11.0, which added new functions to clojure.core (particularly `abs`). Compiled code from an earlier version of Clojure with var names that matched the newly added functions in clojure.core would be unbound when loaded in a 1.11.0 runtime. In addition to [CLJ-2711](https://clojure.atlassian.net/browse/CLJ-2711), we rolled back a previous fix in this area ([CLJ-1604](https://clojure.atlassian.net/browse/CLJ-1604)). + +See: [CLJ-2711](https://clojure.atlassian.net/browse/CLJ-2711) + +## 3 Fixes and enhancements + +### 3.1 Reader and Compiler + +* [CLJ-2726](https://clojure.atlassian.net/browse/CLJ-2726) `#uuid` data reader - Fix exception on invalid input so it flows through reader +* [CLJ-2813](https://clojure.atlassian.net/browse/CLJ-2813) anonymous function arg reader - no longer accepts invalid arg symbols +* [CLJ-2843](https://clojure.atlassian.net/browse/CLJ-2843) Reflective calls to Java methods that take primitive long or double now work when passed a narrower boxed number at runtime (Integer, Short, Byte, Float). Previously, these methods were not matched during reflection and an error was thrown. +* [CLJ-2145](https://clojure.atlassian.net/browse/CLJ-2145) - Fix clearing of closed overs in `^:once` fns +* [CLJ-2317](https://clojure.atlassian.net/browse/CLJ-2317) - `recur` to head of `:once` fn cancels once + +### 3.2 Core + +* [CLJ-2739](https://clojure.atlassian.net/browse/CLJ-2739) ArityException - Fix message when function incorrectly called with >20 args +* [CLJ-2709](https://clojure.atlassian.net/browse/CLJ-2709) `range` - Use optimized range for int args +* [CLJ-2721](https://clojure.atlassian.net/browse/CLJ-2721) `range` - Fix invalid arg order when adding meta to non-optimized range +* [CLJ-2683](https://clojure.atlassian.net/browse/CLJ-2683) `with-open` - Fix to not qualify `.close` method on expansion +* [CLJ-2724](https://clojure.atlassian.net/browse/CLJ-2724) `clojure.java.io/do-copy` - Fix incorrect type hint +* [CLJ-2640](https://clojure.atlassian.net/browse/CLJ-2640) `ex-info` - now handles nil data map +* [CLJ-2717](https://clojure.atlassian.net/browse/CLJ-2717) `nthrest` now returns rest output on n=0 or past end of seq +* [CLJ-1872](https://clojure.atlassian.net/browse/CLJ-1872) `empty?` - adds support for `counted?` collections +* [CLJ-2694](https://clojure.atlassian.net/browse/CLJ-2694) Fix ratio invariants violated when using Long/MIN_VALUE +* [CLJ-2568](https://clojure.atlassian.net/browse/CLJ-2568) `clojure.walk/walk` - preserve metadata on lists and seqs +* [CLJ-2686](https://clojure.atlassian.net/browse/CLJ-2686) `clojure.core.server/parse-props` - Fix exception if system properties concurrently modified during initialization +* [CLJ-2645](https://clojure.atlassian.net/browse/CLJ-2645) `PrintWriter-on` should support auto-flush, and prepl should use it for the err stream +* [CLJ-2698](https://clojure.atlassian.net/browse/CLJ-2698) `defprotocol` - ignore unused primitive return type hints +* [CLJ-2783](https://clojure.atlassian.net/browse/CLJ-2783) replace calls to deprecated URL constructor + +### 3.3 Docstrings + +* [CLJ-2225](https://clojure.atlassian.net/browse/CLJ-2225) `assert` and `\*assert*` - improve docstrings to add context +* [CLJ-2290](https://clojure.atlassian.net/browse/CLJ-2290) `into` - add 0- and 1-arity to docstring +* [CLJ-2552](https://clojure.atlassian.net/browse/CLJ-2552) `reify` - improve docstring and fix example +* [CLJ-1385](https://clojure.atlassian.net/browse/CLJ-1385) `transient` - include usage model from reference docs + +# Changes to Clojure in Version 1.11.4 + +* [CLJ-2145](https://clojure.atlassian.net/browse/CLJ-2145) - Fix clearing of closed overs in `^:once` fns +* [CLJ-2317](https://clojure.atlassian.net/browse/CLJ-2317) - `recur` to head of `:once` fn cancels once + +# Changes to Clojure in Version 1.11.3 + +* [CLJ-2843](https://clojure.atlassian.net/browse/CLJ-2843) - Reflective calls to Java methods that take primitive long or double now work when passed a narrower boxed number at runtime (Integer, Short, Byte, Float). Previously, these methods were not matched during reflection and an error was thrown. + +# Changes to Clojure in Version 1.11.2 + +Fixes [CVE-2024-22871](https://nvd.nist.gov/vuln/detail/CVE-2024-22871) detailed in [GHSA-vr64-r9qj-h27f](https://github.com/advisories/GHSA-vr64-r9qj-h27f): + +* [CLJ-2839](https://clojure.atlassian.net/browse/CLJ-2839) - `iterate`, `cycle`, `repeat` - infinite seqs have infinite `hashCode()` + +# 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 + +* [CLJ-2564](https://clojure.atlassian.net/browse/CLJ-2564) + Improve error message for case + +## 2 Fixes + +* [CLJ-2453](https://clojure.atlassian.net/browse/CLJ-2453) + Enable reader conditionals in Clojure prepl + +# Changes to Clojure in Version 1.10.2 + +## 1 Dependencies + +Updated dependencies: + +* spec.alpha dependency to 0.2.194 - [changes](https://github.com/clojure/spec.alpha/blob/master/CHANGES.md) +* core.specs.alpha dependency to 0.2.56 - [changes](https://github.com/clojure/core.specs.alpha/blob/master/CHANGES.md) + +## 2 Fixes + +## 2.1 Interop / JVM + +* [CLJ-1472](https://clojure.atlassian.net/browse/CLJ-1472) + Ensure monitor object is on stack, for verifiers +* [CLJ-2517](https://clojure.atlassian.net/browse/CLJ-2517) + More fixes for invocation of static interface methods with primitive args +* [CLJ-2492](https://clojure.atlassian.net/browse/CLJ-2492) + Remove uses of deprecated Class.newInstance() +* [CLJ-2534](https://clojure.atlassian.net/browse/CLJ-2534) + Fix javadoc urls for JDK 11+ +* [CLJ-2571](https://clojure.atlassian.net/browse/CLJ-2571) + Add Throwable return type hint to ex-cause +* [CLJ-2572](https://clojure.atlassian.net/browse/CLJ-2572) + Avoid reflection in clojure.data +* [CLJ-2502](https://clojure.atlassian.net/browse/CLJ-2502) + Fix reflection warnings in clojure.stacktrace/print-stack-trace +* [CLJ-2597](https://clojure.atlassian.net/browse/CLJ-2597) + proxy should emit Java 1.8 bytecode + +## 2.2 Core + +* [CLJ-2580](https://clojure.atlassian.net/browse/CLJ-2580) + Fix case expression branch analysis that resulted in compilation error +* [CLJ-2564](https://clojure.atlassian.net/browse/CLJ-2564) + Improve error message for case +* [CLJ-2585](https://clojure.atlassian.net/browse/CLJ-2585) + nth with not-found on regex matcher returns not-found on last group index +* [CLJ-1364](https://clojure.atlassian.net/browse/CLJ-1364) + vector-of does not implement equals or hashing methods +* [CLJ-2549](https://clojure.atlassian.net/browse/CLJ-2549) + vector-of does not implement IObj for metadata +* [CLJ-1187](https://clojure.atlassian.net/browse/CLJ-1187) + quoted metadata on empty literal colls is lost +* [CLJ-2459](https://clojure.atlassian.net/browse/CLJ-2459) + ExceptionInInitializerError if jars executed with java -jar + +## 2.3 Printing + +* [CLJ-2469](https://clojure.atlassian.net/browse/CLJ-2469) + Fix errors in printing some maps with namespace syntax +* [CLJ-1445](https://clojure.atlassian.net/browse/CLJ-1445) + pprint doesn't print collection metadata when `*print-meta*` is true + +## 2.4 Docstrings + +* [CLJ-2295](https://clojure.atlassian.net/browse/CLJ-2295) + Eliminate duplicate doc string printing for special forms +* [CLJ-2495](https://clojure.atlassian.net/browse/CLJ-2495) + prepl docstring is incorrect +* [CLJ-2169](https://clojure.atlassian.net/browse/CLJ-2169) + conj has out-of-date :arglists + +## 3 Performance + +* [CLJ-1005](https://clojure.atlassian.net/browse/CLJ-1005) + Use transient map in zipmap + # Changes to Clojure in Version 1.10.1 ## 1 Features and Major Changes 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_fn_adapter_tests.clj b/codegen/gen_fn_adapter_tests.clj new file mode 100644 index 0000000000..423ee77fc6 --- /dev/null +++ b/codegen/gen_fn_adapter_tests.clj @@ -0,0 +1,177 @@ +;; This code was used to generate: +;; generated_all_fi_adapters_in_let.clj +;; generated_functional_adapters_in_def_requiring_reflection.clj +;; generated_functional_adapters_in_def.clj +;; AdapterExerciser.java +;; This code is not intended to be reused but might be +;; useful in the future as a template for other code gen. + +(ns gen-fn-adapter-tests + (:require + [clojure.string :as str]) + (:import + [java.io StringWriter Writer])) + +(defn let-test-header [imported-methods] + (format " +(ns clojure.test-clojure.generated-all-fi-adapters-in-let + (:use clojure.test) + (:require [clojure.string :as str]) + (:import (clojure.test AdapterExerciser %s))) + + (deftest test-all-fi-adapters-in-let + (let [^AdapterExerciser exerciser (AdapterExerciser.)" imported-methods)) + +(defn def-test-header [filename] + (format " +(ns clojure.test-clojure.%s + (:use clojure.test) + (:require [clojure.string :as str]) + (:import (clojure.test AdapterExerciser))) + + (deftest functional-adapters-in-def + (def exerciser (AdapterExerciser.))" filename)) + +(def adapter-exerciser-header " +package clojure.test; + +public class AdapterExerciser {") + +(defn sigs [args return-types] + (let [fun-sig-reducer (fn [res ret] + (mapcat seq [res (map (fn [params] + (str params ret)) args)]))] + (reduce fun-sig-reducer [] return-types))) + +(defn gen-sigs [] + (let [small-rets ["L" "I" "S" "B" "D" "F" "O"] + zero-arity (sigs [""] small-rets) + single-arity (sigs ["L" "D" "O"] small-rets) + two-arity (sigs ["LL" "LO" "OL" "DD" "LD" "DL" "OO" "OD" "DO"] small-rets) + big-rets ["O"] + three-arity (sigs ["OOO"] big-rets) + four-arity (sigs ["OOOO"] big-rets) + five-arity (sigs ["OOOOO"] big-rets) + six-arity (sigs ["OOOOOO"] big-rets) + seven-arity (sigs ["OOOOOOO"] big-rets) + eight-arity (sigs ["OOOOOOOO"] big-rets) + nine-arity (sigs ["OOOOOOOOO"] big-rets) + ten-arity (sigs ["OOOOOOOOOO"] big-rets)] + (mapcat seq [zero-arity single-arity two-arity three-arity four-arity five-arity six-arity seven-arity eight-arity nine-arity ten-arity]))) + +(def alphabet (map char (range 97 122))) +(def type-hints {:D "^double " + :O "^AdapterExerciser " + :L "^long " + :I "^int " + :F "^float " + :Z "^boolean " + :S "^short " + :B "^byte "}) +(def types {:D "double" + :O "AdapterExerciser" + :L "long" + :I "int" + :F "float" + :Z "boolean" + :S "short" + :B "byte"}) +(def method-args {:D "(double 1)" + :O "exerciser" + :L "(long 1)" + :I "1" + :F "(float 1)" + :Z "false" + :S "(short 1)" + :B "(byte 1)"}) + +(defn format-parts [sig] + (let [return-type-initial (str (last sig)) + return-type (get types (keyword return-type-initial)) + input-types (map str (butlast sig)) + arg-type-hints (map #(get type-hints (keyword %)) input-types) + java-types (map #(get types (keyword %)) input-types) + fn-vars (str/join " " (map #(str %1 %2) arg-type-hints (take (count input-types) alphabet))) + fn-args (str/join " " (map #(get method-args (keyword %)) input-types)) + java-vars (str/join ", " (map #(str %1 " " %2) java-types (take (count input-types) alphabet))) + fn-body (get method-args (keyword return-type-initial)) + expected-val (get method-args (keyword return-type-initial))] + {:return-type return-type :fn-args fn-args :return-type-initial return-type-initial :fn-vars fn-vars :fn-body fn-body :input-types input-types :java-vars java-vars :expected-val expected-val})) + +(defn gen-imported-methods [sigs] + (let [sb (StringBuilder. " ")] + (doseq [sig sigs] + (.append sb (format "AdapterExerciser$%s" sig)) + (.append sb "\n")) + (.toString sb))) + +(defn gen-test-all-fi-adapters-in-let [] + (let [adapter-signatures (gen-sigs) + imported-methods (gen-imported-methods adapter-signatures) + sb (StringBuilder. ^String (let-test-header imported-methods))] + ;; Assemble let + (doseq [sig adapter-signatures] + (let [{:keys [fn-vars fn-body]} (format-parts sig)] + (.append sb "\n") + (.append sb (format " ^AdapterExerciser$%s %sadapter (fn [%s] %s)" sig sig fn-vars fn-body)))) + (.append sb "]") + ;; Assemble test cases + (doseq [sig adapter-signatures] + (let [{:keys [return-type-initial fn-args expected-val]} (format-parts sig)] + (.append sb "\n") + (.append sb (format " (is (= (.takes%sRet%s %sadapter %s) %s))" (str/join "" (butlast sig)) return-type-initial sig fn-args expected-val)))) + (.append sb "))") + (spit "generated_all_fi_adapters_in_let.clj" (.toString sb)))) + +(defn gen-test-functional-adapters-in-def [] + (let [sb (StringBuilder. ^String (def-test-header "generated-functional-adapters-in-def")) + adapter-signatures (gen-sigs)] + (doseq [sig adapter-signatures] + (let [{:keys [fn-vars fn-body]} (format-parts sig)] + (.append sb "\n") + (.append sb (format " (def %sadapter (fn [%s] %s))" sig fn-vars fn-body)) + (.append sb "\n") + (.append sb (format " (is (= (.method%s ^AdapterExerciser exerciser %sadapter) %s))" sig sig (str "\"" sig "\""))))) + (.append sb ")") + (spit "generated_functional_adapters_in_def.clj" (.toString sb)))) + +(defn gen-test-functional-adapters-in-def-requiring-reflection [] + (let [sb (StringBuilder. ^String (def-test-header "generated-functional-adapters-in-def-requiring-reflection")) + adapter-signatures (gen-sigs)] + (doseq [sig adapter-signatures] + (let [{:keys [fn-vars fn-body]} (format-parts sig)] + (.append sb "\n") + (.append sb (format " (def %sadapter (fn [%s] %s))" sig fn-vars fn-body)) + (.append sb "\n") + (.append sb (format " (is (= (.method%s exerciser %sadapter) %s))" sig sig (str "\"" sig "\""))))) + (.append sb ")") + (spit "generated_functional_adapters_in_def_requiring_reflection.clj" (.toString sb)))) + +(defn gen-adapter-exerciser-class [] + (let [sb (StringBuilder. ^String adapter-exerciser-header) + adapter-signatures (gen-sigs)] + (doseq [sig adapter-signatures] + (let [{:keys [return-type return-type-initial input-types java-vars]} (format-parts sig)] + (.append sb "\n") + (.append sb " @FunctionalInterface\n") + (.append sb (format " public interface %s {\n" sig)) + (.append sb (format " public %s takes%sRet%s(%s);\n" return-type (str/join "" input-types) return-type-initial java-vars)) + (.append sb " }"))) + (doseq [sig adapter-signatures] + (.append sb "\n") + (.append sb (format " public String method%s(%s a) { return %s; }" sig sig (str "\"" sig "\"")))) + (.append sb "}") + (spit "AdapterExerciser.java" (.toString sb)))) + +(defn gen-all [] + (gen-test-all-fi-adapters-in-let) + (gen-test-functional-adapters-in-def) + (gen-test-functional-adapters-in-def-requiring-reflection) + (gen-adapter-exerciser-class)) + +(comment + (gen-all) + (gen-test-all-fi-adapters-in-let) + (gen-test-functional-adapters-in-def) + (gen-test-functional-adapters-in-def-requiring-reflection) + (gen-adapter-exerciser-class)) \ No newline at end of file diff --git a/codegen/gen_fn_invokers.clj b/codegen/gen_fn_invokers.clj new file mode 100644 index 0000000000..3d2a8a73ca --- /dev/null +++ b/codegen/gen_fn_invokers.clj @@ -0,0 +1,173 @@ +;; This code was used to generate the clojure.lang.FnInvokers class in +;; Clojure 1.12. This code is not intended to be reused but might be +;; useful in the future as a template for other code gen. + +(ns gen-fn-invokers + (:require + [clojure.string :as str])) + +(def 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 (https://opensource.org/license/epl-1-0) + * which can be found in the file epl-v10.html at the root of this distribution. + * By using this software in any fashion, you are agreeing to be bound by + * the terms of this license. + * You must not remove this notice, or any other, from this software. + **/ + +package clojure.lang; + +public class FnInvokers { + + // Encode invoker param/return class to code for method name + static char encodeInvokerType(Class c) { + if(Long.TYPE.equals(c)) { + return 'L'; + } else if(Double.TYPE.equals(c)) { + return 'D'; + } else if(Integer.TYPE.equals(c)) { + return 'I'; + } else if(Short.TYPE.equals(c)) { + return 'S'; + } else if(Byte.TYPE.equals(c)) { + return 'B'; + } else if(Float.TYPE.equals(c)) { + return 'F'; + } else { + return 'O'; + } + } + +") + +(def footer + "}") + +(def invokeO-format + " public static Object invoke%sO(IFn f0%s) { + return f0.invoke(%s); + }") + +(def invokeO-with-l-or-d-arg-format + " public static Object invoke%sO(IFn f0%s) { + if(f0 instanceof IFn.%sO) { + return ((IFn.%sO)f0).invokePrim(%s); + } else { + return f0.invoke(%s); + } + }") + +(def invokeD-format + " public static double invoke%sD(IFn f0%s) { + if(f0 instanceof IFn.%sD) { + return ((IFn.%sD)f0).invokePrim(%s); + } else { + return RT.doubleCast(f0.invoke(%s)); + } + }") + +(def invokeF-format + " public static float invoke%sF(IFn f0%s) { + if(f0 instanceof IFn.%sD) { + return RT.floatCast(((IFn.%sD)f0).invokePrim(%s)); + } else { + return RT.floatCast(f0.invoke(%s)); + } + }") + +(def invokeL-format + " public static long invoke%sL(IFn f0%s) { + if(f0 instanceof IFn.%sL) { + return ((IFn.%sL)f0).invokePrim(%s); + } else { + return RT.longCast(f0.invoke(%s)); + } + }") + +(def invokeI-format + " public static int invoke%sI(IFn f0%s) { + if(f0 instanceof IFn.%sL) { + return RT.intCast(((IFn.%sL)f0).invokePrim(%s)); + } else { + return RT.intCast(f0.invoke(%s)); + } + }") + +(def invokeS-format + " public static short invoke%sS(IFn f0%s) { + if(f0 instanceof IFn.%sL) { + return RT.shortCast(((IFn.%sL)f0).invokePrim(%s)); + } else { + return RT.shortCast(f0.invoke(%s)); + } + }") + +(def invokeB-format + " public static byte invoke%sB(IFn f0%s) { + if(f0 instanceof IFn.%sL) { + return RT.byteCast(((IFn.%sL)f0).invokePrim(%s)); + } else { + return RT.byteCast(f0.invoke(%s)); + } + }") + +(def alphabet (map char (range 97 122))) + +(def arg-types {:D ", double " + :L ", long " + :O ", Object "}) + +(defn gen-invoke [sig] + (let [formatter (str (last sig)) + args (map str (butlast sig)) + arg-types (map #(get arg-types (keyword %)) args) + fn-vars (str/join "" (map #(str %1 %2) arg-types (take (count args) alphabet))) + fn-vars-sans-type (str/join ", " (take (count args) alphabet)) + arg-str (str/join args)] + (case formatter + "O" (if (some #{"D" "L"} args) + (format invokeO-with-l-or-d-arg-format arg-str fn-vars arg-str arg-str fn-vars-sans-type fn-vars-sans-type) + (format invokeO-format arg-str fn-vars fn-vars-sans-type)) + "L" (format invokeL-format arg-str fn-vars arg-str arg-str fn-vars-sans-type fn-vars-sans-type) + "I" (format invokeI-format arg-str fn-vars arg-str arg-str fn-vars-sans-type fn-vars-sans-type) + "S" (format invokeS-format arg-str fn-vars arg-str arg-str fn-vars-sans-type fn-vars-sans-type) + "B" (format invokeB-format arg-str fn-vars arg-str arg-str fn-vars-sans-type fn-vars-sans-type) + "D" (format invokeD-format arg-str fn-vars arg-str arg-str fn-vars-sans-type fn-vars-sans-type) + "F" (format invokeF-format arg-str fn-vars arg-str arg-str fn-vars-sans-type fn-vars-sans-type)))) + +(defn sigs [args return-types] + (let [fun-sig-reducer (fn [res ret] + (mapcat seq [res (map (fn [params] + (str params ret)) args)]))] + (reduce fun-sig-reducer [] return-types))) + +(defn gen-sigs [] + (let [small-rets ["L" "I" "S" "B" "D" "F" "O"] + zero-arity (sigs [""] small-rets) + single-arity (sigs ["L" "D" "O"] small-rets) + two-arity (sigs ["LL" "LO" "OL" "DD" "LD" "DL" "OO" "OD" "DO"] small-rets) + big-rets ["O"] + three-arity (sigs ["OOO"] big-rets) + four-arity (sigs ["OOOO"] big-rets) + five-arity (sigs ["OOOOO"] big-rets) + six-arity (sigs ["OOOOOO"] big-rets) + seven-arity (sigs ["OOOOOOO"] big-rets) + eight-arity (sigs ["OOOOOOOO"] big-rets) + nine-arity (sigs ["OOOOOOOOO"] big-rets) + ten-arity (sigs ["OOOOOOOOOO"] big-rets)] + (mapcat seq [zero-arity single-arity two-arity three-arity four-arity five-arity six-arity seven-arity eight-arity nine-arity ten-arity]))) + +(defn gen-invokers [] + (let [sb (StringBuilder. ^String header) + invoker-signatures (gen-sigs)] + (doseq [sig invoker-signatures] + (.append sb (gen-invoke sig)) + (.append sb "\n\n")) + (.append sb footer) + (spit "src/jvm/clojure/lang/FnInvokers.java" (.toString sb)))) + +(comment + (gen-invokers) + ) \ 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 78784d3a4d..3f8637f5bf 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ clojure clojure jar - 1.10.2-master-SNAPSHOT + 1.13.0-master-SNAPSHOT http://clojure.org/ Clojure core environment and runtime library. @@ -41,17 +41,17 @@ org.clojure spec.alpha - 0.2.187 + 0.5.238 org.clojure core.specs.alpha - 0.2.44 + 0.4.74 org.clojure test.generative - 0.5.2 + 1.1.0 test @@ -63,7 +63,7 @@ org.clojure test.check - 0.9.0 + 1.1.1 test @@ -75,16 +75,19 @@ javax.xml.ws jaxws-api - 2.3.0 + 2.3.1 test + + central + https://central.sonatype.com + - - sonatype-nexus-staging - https://oss.sonatype.org/content/repositories/snapshots + central-snapshot + https://central.sonatype.com/repository/maven-snapshots/ @@ -103,7 +106,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.1 + 3.13.0 1.8 1.8 @@ -112,7 +115,7 @@ maven-antrun-plugin - 1.6 + 3.1.0 clojure-compile @@ -142,10 +145,11 @@ + org.codehaus.mojo build-helper-maven-plugin - 1.5 + 3.5.0 add-clojure-source-dirs @@ -163,7 +167,7 @@ maven-assembly-plugin - 2.2 + 3.7.1 clojure-slim-jar @@ -175,29 +179,17 @@ src/assembly/slim.xml - - - clojure.main - - maven-jar-plugin - 2.3.1 - - - - clojure.main - - - + 3.4.1 maven-source-plugin - 2.1.2 + 3.3.1 attach-sources @@ -219,7 +211,7 @@ instead, push SCM changes in Hudson configuration --> org.apache.maven.plugins maven-release-plugin - 2.4.1 + 3.0.1 false true @@ -229,23 +221,39 @@ org.apache.maven.plugins maven-surefire-plugin - 2.6 + 3.2.5 true - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.7 + + org.apache.maven.plugins + maven-javadoc-plugin + 3.11.2 + + + attach-javadocs + + jar + + + + + none + + + + + + org.sonatype.central + central-publishing-maven-plugin + 0.7.0 true - - sonatype-nexus-staging - https://oss.sonatype.org/ - true + central + true @@ -273,7 +281,7 @@ maven-assembly-plugin - 2.2 + 3.7.1 clojure-distribution @@ -301,7 +309,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.5 + 3.2.4 sign-artifacts @@ -321,7 +329,7 @@ org.clojure test.check - 0.9.0 + 1.1.1 org.clojure @@ -335,7 +343,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.1.0 + 3.5.3 package diff --git a/readme.txt b/readme.txt index 815cf9126d..12fa75dea8 100644 --- a/readme.txt +++ b/readme.txt @@ -1,24 +1,21 @@ * Clojure * Copyright (c) Rich Hickey. All rights reserved. * The use and distribution terms for this software are covered by the - * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) + * Eclipse Public License 1.0 (https://opensource.org/license/epl-1-0/) * which can be found in the file epl-v10.html at the root of this distribution. * By using this software in any fashion, you are agreeing to be bound by * the terms of this license. * You must not remove this notice, or any other, from this software. Docs: https://clojure.org -Feedback: http://groups.google.com/group/clojure +Feedback: https://ask.clojure.org Getting Started: https://clojure.org/guides/getting_started -To build and run locally with Ant: - - One-time setup: ./antsetup.sh - To build: ant local - To run: java -jar clojure.jar - To build locally with Maven: + To test: + mvn test + To build (output JARs in target/): mvn package @@ -76,7 +73,7 @@ under the Apache License: Apache License Version 2.0, January 2004 - http://www.apache.org/licenses/ + https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj index 8d29cd3862..9694037b50 100644 --- a/src/clj/clojure/core.clj +++ b/src/clj/clojure/core.clj @@ -73,10 +73,12 @@ rest (fn ^:static rest [x] (. clojure.lang.RT (more x)))) (def - ^{:arglists '([coll x] [coll x & xs]) + ^{:arglists '([] [coll] [coll x] [coll x & xs]) :doc "conj[oin]. Returns a new collection with the xs - 'added'. (conj nil item) returns (item). The 'addition' may - happen at different 'places' depending on the concrete type." + 'added'. (conj nil item) returns (item). + (conj coll) returns coll. (conj) returns []. + The 'addition' may happen at different 'places' depending + on the concrete type." :added "1.0" :static true} conj (fn ^:static conj @@ -1132,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" @@ -1446,7 +1460,11 @@ "Returns a function that takes any number of arguments and returns x." {:added "1.0" :static true} - [x] (fn [& args] x)) + [x] (fn + ([] x) + ([_] x) + ([_ _] x) + ([_ _ & args] x))) (defn identity "Returns its argument." @@ -1492,7 +1510,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"} @@ -2909,7 +2928,7 @@ (cons (first s) (take-while pred (rest s)))))))) (defn drop - "Returns a lazy sequence of all but the first n items in coll. + "Returns a laziness-preserving sequence of all but the first n items in coll. Returns a stateful transducer when no collection is provided." {:added "1.0" :static true} @@ -2926,12 +2945,18 @@ result (rf result input)))))))) ([n coll] - (let [step (fn [n coll] - (let [s (seq coll)] - (if (and (pos? n) s) - (recur (dec n) (rest s)) - s)))] - (lazy-seq (step n coll))))) + (if (instance? clojure.lang.IDrop coll) + (or + (if (pos? n) + (.drop ^clojure.lang.IDrop coll (if (int? n) n (Math/ceil n))) + (seq coll)) + ()) + (let [step (fn [n coll] + (let [s (seq coll)] + (if (and (pos? n) s) + (recur (dec n) (rest s)) + s)))] + (lazy-seq (step n coll)))))) (defn drop-last "Return a lazy sequence of all but the last n (default 1) items in coll" @@ -3013,7 +3038,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) ) @@ -3028,15 +3054,15 @@ ([] (iterate inc' 0)) ([end] - (if (instance? Long end) + (if (int? end) (clojure.lang.LongRange/create end) (clojure.lang.Range/create end))) ([start end] - (if (and (instance? Long start) (instance? Long end)) + (if (and (int? start) (int? end)) (clojure.lang.LongRange/create start end) (clojure.lang.Range/create start end))) ([start end step] - (if (and (instance? Long start) (instance? Long end) (instance? Long step)) + (if (and (int? start) (int? end) (int? step)) (clojure.lang.LongRange/create start end step) (clojure.lang.Range/create start end step)))) @@ -3068,22 +3094,6 @@ (reduce1 merge-entry (or m1 {}) (seq m2)))] (reduce1 merge2 maps)))) - - -(defn zipmap - "Returns a map with the keys mapped to the corresponding vals." - {:added "1.0" - :static true} - [keys vals] - (loop [map {} - ks (seq keys) - vs (seq vals)] - (if (and ks vs) - (recur (assoc map (first ks) (first vs)) - (next ks) - (next vs)) - map))) - (defn line-seq "Returns the lines of text from rdr as a lazy sequence of strings. rdr must implement java.io.BufferedReader." @@ -3168,20 +3178,30 @@ {:added "1.0" :static true} [coll n] + (if (instance? clojure.lang.IDrop coll) + (if (pos? n) + (.drop ^clojure.lang.IDrop coll (if (int? n) n (Math/ceil n))) + (seq coll)) (loop [n n xs (seq coll)] (if (and xs (pos? n)) (recur (dec n) (next xs)) - xs))) + xs)))) (defn nthrest "Returns the nth rest of coll, coll when n is 0." {:added "1.3" :static true} [coll n] - (loop [n n xs coll] - (if-let [xs (and (pos? n) (seq xs))] - (recur (dec n) (rest xs)) - xs))) + (if (pos? n) + (or + (if (instance? clojure.lang.IDrop coll) + (.drop ^clojure.lang.IDrop coll (if (int? n) n (Math/ceil n))) + (loop [n n xs coll] + (if-let [xs (and (pos? n) (seq xs))] + (recur (dec n) (rest xs)) + (seq xs)))) + ()) + coll)) (defn partition "Returns a lazy sequence of lists of n items each, at offsets step @@ -3342,7 +3362,15 @@ ;;;;;;;;;;;;;;;;;;;;; editable collections ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn transient - "Returns a new, transient version of the collection, in constant time." + "Returns a new, transient version of the collection, in constant time. + + Transients support a parallel set of 'changing' operations, with similar names + followed by ! - assoc!, conj! etc. These do the same things as their persistent + counterparts except the return values are themselves transient. + + Note in particular that transients are not designed to be bashed in-place. You + must capture and use the return value in the next call. In this way, they support + the same code structure as the functional persistent code they replace." {:added "1.1" :static true} [^clojure.lang.IEditableCollection coll] @@ -3847,7 +3875,7 @@ (try (with-open ~(subvec bindings 2) ~@body) (finally - (. ~(bindings 0) close)))) + (. ~(bindings 0) ~'close)))) :else (throw (IllegalArgumentException. "with-open only allows Symbols in bindings")))) @@ -4385,7 +4413,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] @@ -4433,7 +4473,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) @@ -4482,10 +4526,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 @@ -4513,12 +4562,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] @@ -4817,8 +4868,11 @@ (.getCause ^Throwable ex))) (defmacro assert - "Evaluates expr and throws an exception if it does not evaluate to - logical true." + "Evaluates expression x and throws an AssertionError with optional + message if x does not evaluate to logical true. + + Assertion checks are omitted from compiled code if '*assert*' is + false." {:added "1.0"} ([x] (when *assert* @@ -5936,13 +5990,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)] @@ -5954,13 +6010,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) @@ -5977,7 +6037,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: " @@ -6041,6 +6101,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. @@ -6057,9 +6120,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: @@ -6206,13 +6270,6 @@ fails, attempts to require sym's namespace and retries." ([m k f x y z & more] (assoc m k (apply f (get m k) x y z more)))) -(defn empty? - "Returns true if coll has no items - same as (not (seq coll)). - Please use the idiom (seq x) rather than (not (empty? x))" - {:added "1.0" - :static true} - [coll] (not (seq coll))) - (defn coll? "Returns true if x implements IPersistentCollection" {:added "1.0" @@ -6268,6 +6325,16 @@ fails, attempts to require sym's namespace and retries." :static true} [coll] (instance? clojure.lang.Counted coll)) +(defn empty? + "Returns true if coll has no items. To check the emptiness of a seq, + please use the idiom (seq x) rather than (not (empty? x))" + {:added "1.0" + :static true} + [coll] + (if (counted? coll) + (zero? (count coll)) + (not (seq coll)))) + (defn reversible? "Returns true if coll implements Reversible" {:added "1.0" @@ -6299,6 +6366,11 @@ fails, attempts to require sym's namespace and retries." :added "1.0"} *e) +(def ^:dynamic + ^{:doc "Bound to true in a repl thread" + :added "1.12"} + *repl* false) + (defn trampoline "trampoline can be used to convert algorithms requiring mutual recursion without stack consumption. Calls f with supplied args, if @@ -6452,7 +6524,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* @@ -6530,6 +6605,11 @@ fails, attempts to require sym's namespace and retries." " {:added "1.0"}) +(add-doc-and-meta *assert* + "When set to logical false, 'assert' will omit assertion checks in + compiled code. Defaults to true." + {:added "1.0"}) + (defn future? "Returns true if x is a future" {:added "1.1" @@ -6581,6 +6661,19 @@ fails, attempts to require sym's namespace and retries." ([a b c] (f (if (nil? a) x a) (if (nil? b) y b) (if (nil? c) z c))) ([a b c & ds] (apply f (if (nil? a) x a) (if (nil? b) y b) (if (nil? c) z c) ds))))) +(defn zipmap + "Returns a map with the keys mapped to the corresponding vals." + {:added "1.0" + :static true} + [keys vals] + (loop [map (transient {}) + ks (seq keys) + vs (seq vals)] + (if (and ks vs) + (recur (assoc! map (first ks) (first vs)) + (next ks) + (next vs)) + (persistent! map)))) ;;;;;;; case ;;;;;;;;;;;;; (defn- shift-mask [shift mask x] @@ -6653,7 +6746,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 @@ -6760,8 +6853,6 @@ fails, attempts to require sym's namespace and retries." `(let [~ge ~e] (case* ~ge ~shift ~mask ~default ~imap ~switch-type :hash-identity ~skip-check)))))))) -;; redefine reduce with internal-reduce - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; helper files ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (alter-meta! (find-ns 'clojure.core) assoc :doc "Fundamental library of the Clojure language") (load "core_proxy") @@ -6771,6 +6862,45 @@ fails, attempts to require sym's namespace and retries." (load "core/protocols") (load "gvec") +(defn stream-reduce! + "Works like reduce but takes a java.util.stream.BaseStream as its source. + Honors 'reduced', is a terminal operation on the stream" + {:added "1.12"} + ([f ^java.util.stream.BaseStream s] + (clojure.core.protocols/iterator-reduce! (.iterator s) f)) + ([f init ^java.util.stream.BaseStream s] + (clojure.core.protocols/iterator-reduce! (.iterator s) f init))) + +(defn stream-seq! + "Takes a java.util.stream.BaseStream instance s and returns a seq of its + contents. This is a terminal operation on the stream." + {:added "1.12"} + [^java.util.stream.BaseStream stream] + (iterator-seq (.iterator stream))) + +(defn stream-transduce! + "Works like transduce but takes a java.util.stream.BaseStream as its source. + This is a terminal operation on the stream." + {:added "1.12"} + ([xform f ^java.util.stream.BaseStream stream] (stream-transduce! xform f (f) stream)) + ([xform f init ^java.util.stream.BaseStream stream] + (let [f (xform f) + ret (stream-reduce! f init stream)] + (f ret)))) + +(defn stream-into! + "Returns a new coll consisting of coll with all of the items of the + stream conjoined. This is a terminal operation on the stream." + {:added "1.12"} + ([to ^java.util.stream.BaseStream stream] + (if (instance? clojure.lang.IEditableCollection to) + (with-meta (persistent! (stream-reduce! conj! (transient to) stream)) (meta to)) + (stream-reduce! conj to stream))) + ([to xform ^java.util.stream.BaseStream stream] + (if (instance? clojure.lang.IEditableCollection to) + (with-meta (persistent! (stream-transduce! xform conj! (transient to) stream)) (meta to)) + (stream-transduce! xform conj to stream)))) + (defmacro ^:private when-class [class-name & body] `(try (Class/forName ^String ~class-name) @@ -6810,6 +6940,14 @@ 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)) + +;; redefine reduce with internal-reduce (defn reduce "f should be a function of 2 arguments. If val is not supplied, returns the result of applying f to the first 2 items in coll, then @@ -6837,13 +6975,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))) @@ -6888,8 +7031,9 @@ fails, attempts to require sym's namespace and retries." (f ret)))) (defn into - "Returns a new coll consisting of to-coll with all of the items of - from-coll conjoined. A transducer may be supplied." + "Returns a new coll consisting of to with all of the items of + from conjoined. A transducer may be supplied. + (into x) returns x. (into) returns []." {:added "1.0" :static true} ([] []) @@ -6900,7 +7044,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 @@ -7055,7 +7203,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) @@ -7274,6 +7423,50 @@ fails, attempts to require sym's namespace and retries." (let [seg (doall (take n s))] (cons seg (partition-all n step (nthrest s step)))))))) +(defn splitv-at + "Returns a vector of [(into [] (take n) coll) (drop n coll)]" + {:added "1.12"} + [n coll] + [(into [] (take n) coll) (drop n coll)]) + +(defn partitionv + "Returns a lazy sequence of vectors of n items each, at offsets step + apart. If step is not supplied, defaults to n, i.e. the partitions + do not overlap. If a pad collection is supplied, use its elements as + necessary to complete last partition upto n items. In case there are + not enough padding elements, return a partition with less than n items." + {:added "1.12"} + ([n coll] + (partitionv n n coll)) + ([n step coll] + (lazy-seq + (when-let [s (seq coll)] + (let [p (into [] (take n) s)] + (when (= n (count p)) + (cons p (partitionv n step (nthrest s step)))))))) + ([n step pad coll] + (lazy-seq + (when-let [s (seq coll)] + (let [p (into [] (take n) s)] + (if (= n (count p)) + (cons p (partitionv n step pad (nthrest s step))) + (list (into [] (take n) (concat p pad))))))))) + +(defn partitionv-all + "Returns a lazy sequence of vector partitions, but may include + partitions with fewer than n items at the end. + Returns a stateful transducer when no collection is provided." + {:added "1.12"} + ([n] + (partition-all n)) + ([n coll] + (partitionv-all n n coll)) + ([n step coll] + (lazy-seq + (when-let [s (seq coll)] + (let [seg (into [] (take n) coll)] + (cons seg (partitionv-all n step (drop step s)))))))) + (defn shuffle "Return a random permutation of coll" {:added "1.2" @@ -7422,8 +7615,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] @@ -7462,8 +7655,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] @@ -7718,6 +7911,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" @@ -7893,3 +8132,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/protocols.clj b/src/clj/clojure/core/protocols.clj index 10c022911d..2ed43152b6 100644 --- a/src/clj/clojure/core/protocols.clj +++ b/src/clj/clojure/core/protocols.clj @@ -30,27 +30,26 @@ (let [s (seq coll)] (internal-reduce s f val)))) -(defn- iter-reduce - ([^java.lang.Iterable coll f] - (let [iter (.iterator coll)] +;; mutates the iterator, respects reduced +(defn iterator-reduce! + ([^java.util.Iterator iter f] + (if (.hasNext iter) + (iterator-reduce! iter f (.next iter)) + (f))) + ([^java.util.Iterator iter f val] + (loop [ret val] (if (.hasNext iter) - (loop [ret (.next iter)] - (if (.hasNext iter) - (let [ret (f ret (.next iter))] - (if (reduced? ret) - @ret - (recur ret))) - ret)) - (f)))) - ([^java.lang.Iterable coll f val] - (let [iter (.iterator coll)] - (loop [ret val] - (if (.hasNext iter) - (let [ret (f ret (.next iter))] - (if (reduced? ret) - @ret - (recur ret))) - ret))))) + (let [ret (f ret (.next iter))] + (if (reduced? ret) + @ret + (recur ret))) + ret)))) + +(defn- iter-reduce + ([^Iterable coll f] + (iterator-reduce! (.iterator coll) f)) + ([^Iterable coll f val] + (iterator-reduce! (.iterator coll) f val))) (defn- naive-seq-reduce "Reduces a seq, ignoring any opportunities to switch to a more diff --git a/src/clj/clojure/core/server.clj b/src/clj/clojure/core/server.clj index c103888c7b..8f7013fdf0 100644 --- a/src/clj/clojure/core/server.clj +++ b/src/clj/clojure/core/server.clj @@ -16,6 +16,7 @@ [clojure.lang LineNumberingPushbackReader] [java.net InetAddress Socket ServerSocket SocketException] [java.io Reader Writer PrintWriter BufferedWriter BufferedReader InputStreamReader OutputStreamWriter] + [java.util Properties] [java.util.concurrent.locks ReentrantLock])) (set! *warn-on-reflection* true) @@ -51,7 +52,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 @@ -145,14 +146,16 @@ (defn- parse-props "Parse clojure.server.* from properties to produce a map of server configs." - [props] + [^Properties props] (reduce - (fn [acc [^String k ^String v]] - (let [[k1 k2 k3] (str/split k #"\.")] - (if (and (= k1 "clojure") (= k2 "server")) - (conj acc (merge {:name k3} (edn/read-string v))) - acc))) - [] props)) + (fn [acc ^String k] + (let [[k1 k2 k3] (str/split k #"\.")] + (if (and (= k1 "clojure") (= k2 "server")) + (let [v (get props k)] + (conj acc (merge {:name k3} (edn/read-string v)))) + acc))) + [] + (.stringPropertyNames props))) (defn start-servers "Start all servers specified in the system properties." @@ -195,11 +198,11 @@ Calls out-fn with data, one of: {:tag :ret - :val val ;;eval result + :val val ;;eval result, or Throwable->map data if exception thrown :ns ns-name-string :ms long ;;eval time in milliseconds :form string ;;iff successfully read - :clojure.error/phase (:execution et al per clojure.main/ex-triage) ;;iff error occurred + :exception true ;;iff exception thrown } {:tag :out :val string} ;chars from during-eval *out* @@ -220,13 +223,13 @@ (m/with-bindings (in-ns 'user) (binding [*in* (or stdin in-reader) - *out* (PrintWriter-on #(out-fn {:tag :out :val %1}) nil) - *err* (PrintWriter-on #(out-fn {:tag :err :val %1}) nil)] + *out* (PrintWriter-on #(out-fn {:tag :out :val %1}) nil true) + *err* (PrintWriter-on #(out-fn {:tag :err :val %1}) nil true)] (try (add-tap tapfn) (loop [] (when (try - (let [[form s] (read+string in-reader false EOF)] + (let [[form s] (read+string {:eof EOF :read-cond :allow} in-reader)] (try (when-not (identical? form EOF) (let [start (System/nanoTime) @@ -288,7 +291,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..66eeb94d66 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*) @@ -100,18 +101,19 @@ same arity in an interface you must specify complete hints to disambiguate - a missing hint implies Object. - recur works to method heads The method bodies of reify are lexical - closures, and can refer to the surrounding local scope: + Method heads are recursion points for recur, as in a fn. The method + bodies of reify are lexical closures, and can refer to the surrounding + local scope: (str (let [f \"foo\"] - (reify Object - (toString [this] f)))) + (reify Object + (toString [this] f)))) == \"foo\" (seq (let [f \"foo\"] - (reify clojure.lang.Seqable - (seq [this] (seq f))))) - == (\\f \\o \\o)) + (reify clojure.lang.Seqable + (seq [this] (seq f))))) + == (\\f \\o \\o) reify always implements clojure.lang.IObj and transfers meta data of the form to the created object. @@ -651,7 +653,17 @@ [opts sigs])) sigs (when sigs (reduce1 (fn [m s] - (let [name-meta (meta (first s)) + (let [disallowed? '#{int long float double char short byte boolean void} + array-tag? '#{ints longs floats doubles chars shorts bytes booleans objects} + resolve-class-symbol (fn [tag] + (when-not (disallowed? tag) + (if-let [c (and (instance? clojure.lang.Symbol tag) + (= (.indexOf (.getName ^clojure.lang.Symbol tag) ".") -1) + (not (array-tag? tag)) + (resolve tag))] + (symbol (.getName c)) + tag))) + name-meta (update-in (meta (first s)) [:tag] resolve-class-symbol) mname (with-meta (first s) nil) [arglists doc] (loop [as [] rs (rest s)] diff --git a/src/clj/clojure/core_print.clj b/src/clj/clojure/core_print.clj index b4992aadae..7e59c9c809 100644 --- a/src/clj/clojure/core_print.clj +++ b/src/clj/clojure/core_print.clj @@ -226,14 +226,14 @@ (print-meta v w) (print-sequential "[" pr-on " " "]" v w)) -(defn- print-prefix-map [prefix m print-one w] +(defn- print-prefix-map [prefix kvs print-one w] (print-sequential (str prefix "{") - (fn [e ^Writer w] - (do (print-one (key e) w) (.append w \space) (print-one (val e) w))) + (fn [[k v] ^Writer w] + (do (print-one k w) (.append w \space) (print-one v w))) ", " "}" - (seq m) w)) + kvs w)) (defn- print-map [m print-one w] (print-prefix-map nil m print-one w)) @@ -245,26 +245,26 @@ (keyword nil (name named)))) (defn- lift-ns - "Returns [lifted-ns lifted-map] or nil if m can't be lifted." + "Returns [lifted-ns lifted-kvs] or nil if m can't be lifted." [m] (when *print-namespace-maps* (loop [ns nil [[k v :as entry] & entries] (seq m) - lm {}] + kvs []] (if entry - (when (or (keyword? k) (symbol? k)) + (when (qualified-ident? k) (if ns (when (= ns (namespace k)) - (recur ns entries (assoc lm (strip-ns k) v))) + (recur ns entries (conj kvs [(strip-ns k) v]))) (when-let [new-ns (namespace k)] - (recur new-ns entries (assoc lm (strip-ns k) v))))) - [ns (apply conj (empty m) lm)])))) + (recur new-ns entries (conj kvs [(strip-ns k) v]))))) + [ns kvs])))) (defmethod print-method clojure.lang.IPersistentMap [m, ^Writer w] (print-meta m w) - (let [[ns lift-map] (lift-ns m)] + (let [[ns lift-kvs] (lift-ns m)] (if ns - (print-prefix-map (str "#:" ns) lift-map pr-on w) + (print-prefix-map (str "#:" ns) lift-kvs pr-on w) (print-map m pr-on w)))) (defmethod print-dup java.util.Map [m, ^Writer w] @@ -380,7 +380,9 @@ Short/TYPE "Short/TYPE"}) (defmethod print-method Class [^Class c, ^Writer w] - (.write w (.getName c))) + (if (.isArray c) + (print-method (clojure.lang.Util/arrayTypeToSymbol c) w) + (.write w (.getName c)))) (defmethod print-dup Class [^Class c, ^Writer w] (cond @@ -559,10 +561,13 @@ (defn ^java.io.PrintWriter PrintWriter-on "implements java.io.PrintWriter given flush-fn, which will be called when .flush() is called, with a string built up since the last call to .flush(). - if not nil, close-fn will be called with no arguments when .close is called" + if not nil, close-fn will be called with no arguments when .close is called. + autoflush? determines if the PrintWriter will autoflush, false by default." {:added "1.10"} - [flush-fn close-fn] - (let [sb (StringBuilder.)] + ([flush-fn close-fn] + (PrintWriter-on flush-fn close-fn false)) + ([flush-fn close-fn autoflush?] + (let [sb (StringBuilder.)] (-> (proxy [Writer] [] (flush [] (when (pos? (.length sb)) @@ -578,4 +583,4 @@ (.append sb ^String str-cbuf ^int off ^int len) (.append sb ^chars str-cbuf ^int off ^int len))))) java.io.BufferedWriter. - java.io.PrintWriter.))) + (java.io.PrintWriter. ^boolean autoflush?))))) diff --git a/src/clj/clojure/core_proxy.clj b/src/clj/clojure/core_proxy.clj index cacd2dce52..46f7b4b868 100644 --- a/src/clj/clojure/core_proxy.clj +++ b/src/clj/clojure/core_proxy.clj @@ -46,7 +46,7 @@ [(Integer/toHexString (hash inames))]))))) (defn- generate-proxy [^Class super interfaces] - (let [cv (new ClassWriter (. ClassWriter COMPUTE_MAXS)) + (let [cv (clojure.lang.Compiler/classWriter) pname (proxy-name super interfaces) cname (.replace pname \. \/) ;(str "clojure/lang/" (gensym "Proxy__")) ctype (. Type (getObjectType cname)) @@ -131,7 +131,7 @@ (. gen (endMethod))))] ;start class definition - (. cv (visit (. Opcodes V1_5) (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_SUPER)) + (. cv (visit (. Opcodes V1_8) (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_SUPER)) cname nil (iname super) (into-array (map iname (cons IProxy interfaces))))) ;add field for fn mappings @@ -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/genclass.clj b/src/clj/clojure/genclass.clj index a4fa0ec5fb..afef85c8ef 100644 --- a/src/clj/clojure/genclass.clj +++ b/src/clj/clojure/genclass.clj @@ -101,15 +101,26 @@ 'char Character/TYPE 'chars (Class/forName "[C")}) -(defn- ^Class the-class [x] - (cond - (class? x) x - (contains? prim->class x) (prim->class x) - :else (let [strx (str x)] - (clojure.lang.RT/classForName - (if (some #{\. \[} strx) - strx - (str "java.lang." strx)))))) +(defn- the-array-class [sym] + (clojure.lang.RT/classForName + (let [cn (namespace sym)] + (clojure.lang.Compiler$HostExpr/buildArrayClassDescriptor + (if (or (clojure.lang.Compiler/primClass (symbol cn)) (some #{\.} cn)) + sym + (symbol (str "java.lang." cn) (name sym))))))) + +(defn- ^Class the-class [x] + (cond + (class? x) x + (symbol? x) (cond (contains? prim->class x) (prim->class x) + (clojure.lang.Compiler$HostExpr/looksLikeArrayClass x) + (the-array-class x) + :else (let [strx (str x)] + (clojure.lang.RT/classForName + (if (some #{\. \[} strx) + strx + (str "java.lang." strx))))) + :else (clojure.lang.RT/classForName x))) ;; someday this can be made codepoint aware (defn- valid-java-method-name @@ -646,14 +657,15 @@ fully-qualified class name given as a string or symbol (such as 'java.lang.String)" [c] - (if (or (instance? Class c) (prim->class c)) - (Type/getType (the-class c)) - (let [strx (str c)] - (Type/getObjectType - (.replace (if (some #{\. \[} strx) - strx - (str "java.lang." strx)) - "." "/"))))) + (let [c (or (and (symbol? c) (clojure.lang.Compiler$HostExpr/maybeArrayClass c)) c)] + (if (or (instance? Class c) (prim->class c)) + (Type/getType (the-class c)) + (let [strx (str c)] + (Type/getObjectType + (.replace (if (some #{\. \[} strx) + strx + (str "java.lang." strx)) + "." "/")))))) (defn- generate-interface [{:keys [name extends methods]}] diff --git a/src/clj/clojure/gvec.clj b/src/clj/clojure/gvec.clj index 6208f55390..590cfcecf9 100644 --- a/src/clj/clojure/gvec.clj +++ b/src/clj/clojure/gvec.clj @@ -10,7 +10,8 @@ (in-ns 'clojure.core) -(import '(clojure.lang Murmur3)) +(import '(clojure.lang Murmur3 IHashEq Sequential Util SeqIterator) + '(java.util List)) (set! *warn-on-reflection* true) @@ -55,7 +56,7 @@ (recur ret (inc i)))) ret)))) -(deftype VecSeq [^clojure.core.ArrayManager am ^clojure.core.IVecImpl vec anode ^int i ^int offset] +(deftype VecSeq [^clojure.core.ArrayManager am ^clojure.core.IVecImpl vec anode ^int i ^int offset ^clojure.lang.IPersistentMap _meta] :no-print true clojure.core.protocols.InternalReduce @@ -82,11 +83,11 @@ (first [_] (.aget am anode offset)) (next [this] (if (< (inc offset) (.alength am anode)) - (new VecSeq am vec anode i (inc offset)) + (new VecSeq am vec anode i (inc offset) nil) (.chunkedNext this))) (more [this] (let [s (.next this)] - (or s (clojure.lang.PersistentList/EMPTY)))) + (or s clojure.lang.PersistentList/EMPTY))) (cons [this o] (clojure.lang.Cons. o this)) (count [this] @@ -120,10 +121,48 @@ (chunkedNext [_] (let [nexti (+ i (.alength am anode))] (when (< nexti (count vec)) - (new VecSeq am vec (.arrayFor vec nexti) nexti 0)))) + (new VecSeq am vec (.arrayFor vec nexti) nexti 0 nil)))) (chunkedMore [this] (let [s (.chunkedNext this)] - (or s (clojure.lang.PersistentList/EMPTY))))) + (or s clojure.lang.PersistentList/EMPTY))) + + clojure.lang.IMeta + (meta [_] + _meta) + + clojure.lang.IObj + (withMeta [_ m] + (new VecSeq am vec anode i offset m)) + +Object + (hashCode [this] + (loop [hash 1 + s (seq this)] + (if s + (let [v (first s)] + (if (nil? v) + (recur (unchecked-multiply-int 31 hash) (next s)) + (recur (unchecked-add-int (unchecked-multiply-int 31 hash) (.hashCode ^Object v)) (next s)))) + hash))) + (equals [this other] + (cond (identical? this other) true + (or (instance? Sequential other) (instance? List other)) + (loop [s this + os (seq other)] + (if (nil? s) + (nil? os) + (if (Util/equals (first s) (first os)) + (recur (next s) (next os)) + false))) + :else false)) + + IHashEq + (hasheq [this] + (Murmur3/hashOrdered this)) + + Iterable + (iterator [this] + (SeqIterator. this))) (defmethod print-method ::VecSeq [v w] ((get (methods print-method) clojure.lang.ISeq) v w)) @@ -296,7 +335,7 @@ (seq [this] (if (zero? cnt) nil - (VecSeq. am this (.arrayFor this 0) 0 0))) + (VecSeq. am this (.arrayFor this 0) 0 0 nil))) clojure.lang.Sequential ;marker, no methods diff --git a/src/clj/clojure/java/basis.clj b/src/clj/clojure/java/basis.clj new file mode 100644 index 0000000000..9567801962 --- /dev/null +++ b/src/clj/clojure/java/basis.clj @@ -0,0 +1,47 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns clojure.java.basis + "The lib basis includes which libraries and versions were loaded both + for direct dependencies and transitive dependencies, as well as the + classpath and possibly other information from the resolution process. + This basis will be known if the runtime was started by the Clojure CLI. + + The Clojure CLI or tools.deps merge a set of deps maps (often from + deps.edn files). Additional runtime modifications are supplied via argmap + keys, provided via alias maps in the merged deps. Deps maps typically have + :paths, :deps, and :aliases keys. + + The basis is a superset of merged deps.edn files with the following + additional keys: + :basis-config - params used to configure basis deps sources, can be + string path, deps map, nil, or :default + :root - default = loaded as a resource from tools.deps) + :user - default = ~/.clojure/deps.edn) + :project - default = ./deps.edn) + :extra - default = nil + :aliases - coll of keyword aliases to include during dep calculation + :argmap - effective argmap (after resolving and merging argmaps from aliases) + :libs - map of lib to coord for all included libraries + :classpath - classpath map, keys are paths (to directory or .jar), values + are maps with source identifier (either :lib-name or :path-key) + :classpath-roots - vector of paths in classpath order (keys of :classpath)" + (:require + [clojure.java.basis.impl :as impl])) + +(defn initial-basis + "Initial runtime basis at launch, nil if unknown (process not started by CLI)" + {:added "1.12"} + [] + @impl/init-basis) + +(defn current-basis + "Return the current basis, which may have been modified since runtime launch." + {:added "1.12"} + [] + @@impl/the-basis) diff --git a/src/clj/clojure/java/basis/impl.clj b/src/clj/clojure/java/basis/impl.clj new file mode 100644 index 0000000000..5402a9e201 --- /dev/null +++ b/src/clj/clojure/java/basis/impl.clj @@ -0,0 +1,51 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns clojure.java.basis.impl + (:require + [clojure.edn :as edn] + [clojure.java.io :as jio]) + (:import + [java.io PushbackReader])) + +(set! *warn-on-reflection* true) + +(defn- read-edn + "Coerce f to a reader via clojure.java.io/reader and read one edn value. + The reader should contain a single value. Empty input returns nil. + The reader will be read to EOF and closed." + [f] + (let [reader (jio/reader f) + EOF (Object.)] + (with-open [rdr (PushbackReader. reader)] + (let [val (edn/read {:default tagged-literal :eof EOF} rdr)] + (if (identical? EOF val) + nil + (if (not (identical? EOF (edn/read {:eof EOF} rdr))) + (throw (ex-info "Invalid file, expected edn to contain a single value." {})) + val)))))) + +(defn- read-basis + "Read basis edn from basis file or throw" + [basis-file] + (when-let [f (jio/file basis-file)] + (when (.exists f) + (read-edn f)))) + +;; delay construction until needed, access via initial-basis +(def init-basis + (delay (read-basis (System/getProperty "clojure.basis")))) + +;; delay construction until needed, access via current-basis +(def the-basis + (delay (atom @init-basis))) + +(defn update-basis! + "Update the runtime basis by applying f with args" + [f & args] + (apply swap! @the-basis f args)) 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/java/io.clj b/src/clj/clojure/java/io.clj index 72a30ed7a6..5b96664e01 100644 --- a/src/clj/clojure/java/io.clj +++ b/src/clj/clojure/java/io.clj @@ -19,8 +19,11 @@ StringReader ByteArrayInputStream BufferedInputStream BufferedOutputStream CharArrayReader Closeable) + (clojure.lang RT) (java.net URI URL MalformedURLException Socket URLDecoder URLEncoder))) +(set! *warn-on-reflection* true) + (def ^{:doc "Type object for a Java primitive byte array." :private true @@ -48,11 +51,11 @@ String (as-file [s] (File. s)) - (as-url [s] (URL. s)) + (as-url [s] (RT/toUrl s)) File (as-file [f] f) - (as-url [f] (.toURL (.toURI f))) + (as-url [f] (RT/toUrl f)) URL (as-url [u] u) @@ -253,12 +256,12 @@ (assoc default-streams-impl :make-input-stream (fn [^String x opts] (try - (make-input-stream (URL. x) opts) + (make-input-stream (RT/toUrl x) opts) (catch MalformedURLException e (make-input-stream (File. x) opts)))) :make-output-stream (fn [^String x opts] (try - (make-output-stream (URL. x) opts) + (make-output-stream (RT/toUrl x) opts) (catch MalformedURLException err (make-output-stream (File. x) opts)))))) @@ -385,7 +388,7 @@ (defmethod do-copy [byte-array-type Writer] [^"[B" input ^Writer output opts] (do-copy (ByteArrayInputStream. input) output opts)) -(defmethod do-copy [byte-array-type File] [^"[B" input ^Writer output opts] +(defmethod do-copy [byte-array-type File] [^"[B" input ^File output opts] (do-copy (ByteArrayInputStream. input) output opts)) (defn copy diff --git a/src/clj/clojure/java/javadoc.clj b/src/clj/clojure/java/javadoc.clj index 2e9e0fbb89..c36acc3de6 100644 --- a/src/clj/clojure/java/javadoc.clj +++ b/src/clj/clojure/java/javadoc.clj @@ -23,7 +23,11 @@ "1.8" "http://docs.oracle.com/javase/8/docs/api/" "9" "http://docs.oracle.com/javase/9/docs/api/" "10" "http://docs.oracle.com/javase/10/docs/api/" - "11" "https://docs.oracle.com/en/java/javase/11/docs/api/java.base/" + "11" "https://docs.oracle.com/en/java/javase/11/docs/api/%s/" + "12" "https://docs.oracle.com/en/java/javase/12/docs/api/%s/" + "13" "https://docs.oracle.com/en/java/javase/13/docs/api/%s/" + "14" "https://docs.oracle.com/en/java/javase/14/docs/api/%s/" + "15" "https://docs.oracle.com/en/java/javase/15/docs/api/%s/" "http://docs.oracle.com/javase/8/docs/api/")) (def ^:dynamic *remote-javadocs* @@ -53,6 +57,16 @@ [package-prefix url] (dosync (commute *remote-javadocs* assoc package-prefix url))) +(defn- fill-in-module-name [^String url ^String classname] + ;; The getModule method was introduced in JDK 9, and did not exist + ;; in earlier JDK versions. Avoid calling it unless its result is + ;; needed. + (if (.contains url "%s") + (let [klass (Class/forName classname) + module-name (.getName (.getModule klass))] + (format url module-name)) + url)) + (defn- javadoc-url "Searches for a URL for the given class name. Tries *local-javadocs* first, then *remote-javadocs*. Returns a string." @@ -69,7 +83,8 @@ ;; If no local file, try remote URLs: (or (some (fn [[prefix url]] (when (.startsWith classname prefix) - (str url url-path ".html"))) + (str (fill-in-module-name url classname) + url-path ".html"))) @*remote-javadocs*) ;; if *feeling-lucky* try a web search (when *feeling-lucky* (str *feeling-lucky-url* url-path ".html")))))) diff --git a/src/clj/clojure/java/process.clj b/src/clj/clojure/java/process.clj new file mode 100644 index 0000000000..734e125797 --- /dev/null +++ b/src/clj/clojure/java/process.clj @@ -0,0 +1,195 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns clojure.java.process + "A process invocation API wrapping the Java process API. + + The primary function is 'start' which starts a process and handles the + streams as directed. It returns the Process object. Use 'exit-ref' to wait + for completion and receive the exit value, and ‘stdout', 'stderr', 'stdin' + to access the process streams. The 'exec' function handles the common case + to 'start' a process, wait for process exit, and return stdout." + (:require + [clojure.java.io :as jio]) + (:import + [java.io File InputStream OutputStream] + [java.lang ProcessBuilder ProcessBuilder$Redirect Process] + [java.util List] + [clojure.lang IDeref IBlockingDeref] + [java.util.concurrent Executors ExecutorService ThreadFactory])) + +(set! *warn-on-reflection* true) + +;; this is built into Java 9, backfilled here for Java 8 +(def ^:private ^File null-file + (delay + (jio/file + (if (.startsWith (System/getProperty "os.name") "Windows") + "NUL" + "/dev/null")))) + +(defn to-file + "Coerce f to a file per clojure.java.io/file and return a ProcessBuilder.Redirect writing to the file. + Set ':append' in opts to append. This can be passed to 'start' in :out or :err." + {:added "1.12"} + ^ProcessBuilder$Redirect [f & {:keys [append] :as opts}] + (let [fo (jio/file f)] + (if append + (ProcessBuilder$Redirect/appendTo fo) + (ProcessBuilder$Redirect/to fo)))) + +(defn from-file + "Coerce f to a file per clojure.java.io/file and return a ProcessBuilder.Redirect reading from the file. + This can be passed to 'start' in :in." + {:added "1.12"} + ^ProcessBuilder$Redirect [f] + (ProcessBuilder$Redirect/from (jio/file f))) + +(defn start + "Start an external command, defined in args. + The process environment vars are inherited from the parent by + default (use :clear-env to clear them). + + If needed, provide options in map as first arg: + :in - a ProcessBuilder.Redirect (default = :pipe) or :inherit + :out - a ProcessBuilder.Redirect (default = :pipe) or :inherit :discard + :err - a ProcessBuilder.Redirect (default = :pipe) or :inherit :discard :stdout + :dir - current directory when the process runs (default=\".\") + :clear-env - if true, remove all inherited parent env vars + :env - {env-var value} of environment variables to set (all strings) + + Returns the java.lang.Process." + {:added "1.12"} + ^Process [& opts+args] + (let [[opts command] (if (map? (first opts+args)) + [(first opts+args) (rest opts+args)] + [{} opts+args]) + {:keys [in out err dir env clear-env] + :or {in :pipe, out :pipe, err :pipe, dir "."}} opts + pb (ProcessBuilder. ^List command) + to-redirect (fn to-redirect + [x] + (case x + :pipe ProcessBuilder$Redirect/PIPE + :inherit ProcessBuilder$Redirect/INHERIT + :discard (ProcessBuilder$Redirect/to @null-file) + ;; in Java 9+, just use ProcessBuilder$Redirect/DISCARD + x))] + (.directory pb (jio/file dir)) + (.redirectInput pb ^ProcessBuilder$Redirect (to-redirect in)) + (.redirectOutput pb ^ProcessBuilder$Redirect (to-redirect out)) + (if + (= err :stdout) (.redirectErrorStream pb true) + (.redirectError pb ^ProcessBuilder$Redirect (to-redirect err))) + (when clear-env + (.clear (.environment pb))) + (when env + (let [pb-env (.environment pb)] + (run! (fn [[k v]] (.put pb-env k v)) env))) + (.start pb))) + +(defn stdin + "Given a process, return the stdin of the external process (an OutputStream)" + {:added "1.12"} + ^OutputStream [^Process process] + (.getOutputStream process)) + +(defn stdout + "Given a process, return the stdout of the external process (an InputStream)" + {:added "1.12"} + ^InputStream [^Process process] + (.getInputStream process)) + +(defn stderr + "Given a process, return the stderr of the external process (an InputStream)" + {:added "1.12"} + ^InputStream [^Process process] + (.getErrorStream process)) + +(defn exit-ref + "Given a Process (the output of 'start'), return a reference that can be + used to wait for process completion then returns the exit value." + {:added "1.12"} + [^Process process] + (reify + IDeref + (deref [_] (long (.waitFor process))) + + IBlockingDeref + (deref [_ timeout-ms timeout-val] + (if (.waitFor process timeout-ms java.util.concurrent.TimeUnit/MILLISECONDS) + (long (.exitValue process)) + timeout-val)))) + +;; A thread factory for daemon threads +(defonce ^:private io-thread-factory + (let [counter (atom 0)] + (reify ThreadFactory + (newThread [_ r] + (doto (Thread. r) + (.setName (str "Clojure Process IO " (swap! counter inc))) + (.setDaemon true)))))) + +;; An ExecutorService for cached, daemon threads +(defonce ^:private io-executor + (Executors/newCachedThreadPool ^ThreadFactory io-thread-factory)) + +(defn io-task + {:skip-wiki true} + [^Runnable f] + (let [f (bound-fn* f) + fut (.submit ^ExecutorService io-executor ^Callable f)] + (reify + clojure.lang.IDeref + (deref [_] (#'clojure.core/deref-future fut)) + clojure.lang.IBlockingDeref + (deref + [_ timeout-ms timeout-val] + (#'clojure.core/deref-future fut timeout-ms timeout-val)) + clojure.lang.IPending + (isRealized [_] (.isDone fut)) + java.util.concurrent.Future + (get [_] (.get fut)) + (get [_ timeout unit] (.get fut timeout unit)) + (isCancelled [_] (.isCancelled fut)) + (isDone [_] (.isDone fut)) + (cancel [_ interrupt?] (.cancel fut interrupt?))))) + +(defn exec + "Execute a command and on successful exit, return the captured output, + else throw RuntimeException. Args are the same as 'start' and options + if supplied override the default 'exec' settings." + {:added "1.12"} + [& opts+args] + (let [[opts command] (if (map? (first opts+args)) + [(first opts+args) (rest opts+args)] + [{} opts+args]) + opts (merge {:err :inherit} opts)] + (let [proc (apply start opts command) + captured (io-task #(slurp (stdout proc))) + exit (deref (exit-ref proc))] + (if (zero? exit) + @captured + (throw (RuntimeException. (str "Process failed with exit=" exit))))))) + +(comment + ;; shell out and inherit the i/o + (start {:out :inherit, :err :stdout} "ls" "-l") + + ;; write out and err to files, wait for process to exit, return exit code + @(exit-ref (start {:out (to-file "out") :err (to-file "err")} "ls" "-l")) + + ;; capture output to string + (-> (start "ls" "-l") stdout slurp) + + ;; with exec + (exec "ls" "-l") + + ;; read input from file + (-> (exec {:in (from-file "deps.edn")} "wc" "-l") clojure.string/trim parse-long) + ) diff --git a/src/clj/clojure/main.clj b/src/clj/clojure/main.clj index e769ee56ea..89baed7f81 100644 --- a/src/clj/clojure/main.clj +++ b/src/clj/clojure/main.clj @@ -73,7 +73,6 @@ " (" (.getFileName el) ":" (.getLineNumber el) ")"))) ;;;;;;;;;;;;;;;;;;; end of redundantly copied from clojure.repl to avoid dep ;;;;;;;;;;;;;; - (defmacro with-bindings "Executes body in the context of thread-local bindings for several vars that often need to be set!: *ns* *warn-on-reflection* *math-context* @@ -356,7 +355,8 @@ by default when a new command-line REPL is started."} repl-requires '[[clojure.repl :refer (source apropos dir pst doc find-doc)] [clojure.java.javadoc :refer (javadoc)] - [clojure.pprint :refer (pp pprint)]]) + [clojure.pprint :refer (pp pprint)] + [clojure.repl.deps :refer (add-libs add-lib sync-deps)]]) (defmacro with-read-known "Evaluates body with *read-eval* set to a \"known\" value, @@ -446,24 +446,25 @@ by default when a new command-line REPL is started."} repl-requires (caught e) (set! *e e))))] (with-bindings - (try - (init) - (catch Throwable e - (caught e) - (set! *e e))) - (prompt) - (flush) - (loop [] - (when-not - (try (identical? (read-eval-print) request-exit) - (catch Throwable e - (caught e) - (set! *e e) - nil)) - (when (need-prompt) - (prompt) - (flush)) - (recur)))))) + (binding [*repl* true] + (try + (init) + (catch Throwable e + (caught e) + (set! *e e))) + (prompt) + (flush) + (loop [] + (when-not + (try (identical? (read-eval-print) request-exit) + (catch Throwable e + (caught e) + (set! *e e) + nil)) + (when (need-prompt) + (prompt) + (flush)) + (recur))))))) (defn load-script "Loads Clojure source from a file or resource given its path. Paths @@ -670,6 +671,6 @@ java -cp clojure.jar clojure.main -i init.clj script.clj args...") (catch Throwable t (report-error t :target "file") (System/exit 1)))) - (finally + (finally (flush)))) diff --git a/src/clj/clojure/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/pprint/dispatch.clj b/src/clj/clojure/pprint/dispatch.clj index 1ef9a578b6..965c0b255a 100644 --- a/src/clj/clojure/pprint/dispatch.clj +++ b/src/clj/clojure/pprint/dispatch.clj @@ -62,8 +62,19 @@ ;;; are a little easier on the stack. (Or, do "real" compilation, a ;;; la Common Lisp) +(declare pprint-map) + +(defn- pprint-meta [obj] + (when *print-meta* + (when-let [m (meta obj)] + (.write ^java.io.Writer *out* "^") + (pprint-map m) + (.write ^java.io.Writer *out* " ") + (pprint-newline :linear)))) + ;;; (def pprint-simple-list (formatter-out "~:<~@{~w~^ ~_~}~:>")) (defn- pprint-simple-list [alis] + (pprint-meta alis) (pprint-logical-block :prefix "(" :suffix ")" (print-length-loop [alis (seq alis)] (when alis @@ -79,6 +90,7 @@ ;;; (def pprint-vector (formatter-out "~<[~;~@{~w~^ ~_~}~;]~:>")) (defn- pprint-vector [avec] + (pprint-meta avec) (pprint-logical-block :prefix "[" :suffix "]" (print-length-loop [aseq (seq avec)] (when aseq @@ -92,6 +104,7 @@ ;;; (def pprint-map (formatter-out "~<{~;~@{~<~w~^ ~_~w~:>~^, ~_~}~;}~:>")) (defn- pprint-map [amap] + (pprint-meta amap) (let [[ns lift-map] (when (not (record? amap)) (#'clojure.core/lift-ns amap)) amap (or lift-map amap) @@ -110,7 +123,17 @@ (pprint-newline :linear) (recur (next aseq)))))))) -(def ^{:private true} pprint-set (formatter-out "~<#{~;~@{~w~^ ~:_~}~;}~:>")) +;;; (def ^{:private true} pprint-set (formatter-out "~<#{~;~@{~w~^ ~:_~}~;}~:>")) +(defn- pprint-set [aset] + (pprint-meta aset) + (pprint-logical-block :prefix "#{" :suffix "}" + (print-length-loop [aseq (seq aset)] + (when aseq + (write-out (first aseq)) + (when (next aseq) + (.write ^java.io.Writer *out* " ") + (pprint-newline :linear) + (recur (next aseq))))))) (def ^{:private true} type-map {"core$future_call" "Future", diff --git a/src/clj/clojure/repl/deps.clj b/src/clj/clojure/repl/deps.clj new file mode 100644 index 0000000000..64b22c713d --- /dev/null +++ b/src/clj/clojure/repl/deps.clj @@ -0,0 +1,96 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. +(ns clojure.repl.deps + "clojure.repl.deps provides facilities for dynamically modifying the available + libraries in the runtime when running at the REPL, without restarting" + (:require + [clojure.java.io :as jio] + [clojure.java.basis :as basis] + [clojure.java.basis.impl :as basis-impl] + [clojure.tools.deps.interop :as tool]) + (:import + [clojure.lang DynamicClassLoader RT] + [java.io File])) + +(set! *warn-on-reflection* true) + +(defn- add-loader-url + "Add url string or URL to the highest level DynamicClassLoader url set." + [url] + (let [u (if (string? url) (RT/toUrl ^String url) url) + loader (loop [loader (.getContextClassLoader (Thread/currentThread))] + (let [parent (.getParent loader)] + (if (instance? DynamicClassLoader parent) + (recur parent) + loader)))] + (if (instance? DynamicClassLoader loader) + (.addURL ^DynamicClassLoader loader u) + (throw (IllegalAccessError. "Context classloader is not a DynamicClassLoader"))))) + +(defn add-libs + "Given lib-coords, a map of lib to coord, will resolve all transitive deps for the libs + together and add them to the repl classpath, unlike separate calls to add-lib." + {:added "1.12"} + [lib-coords] + (when-not *repl* (throw (RuntimeException. "add-libs is only available at the REPL"))) + (let [{:keys [libs] :as basis} (basis/current-basis) + current-libs (set (keys libs)) + lib-coords (reduce-kv #(if (contains? current-libs %2) %1 (assoc %1 %2 %3)) + {} lib-coords)] + (when-not (empty? lib-coords) + (let [procurer (reduce-kv (fn [m k v] (if (contains? #{"mvn" "git" "local"} (namespace k)) (assoc m k v) m)) {} basis) + tool-args {:existing libs, :add lib-coords, :procurer procurer} + {:keys [added] :as _res} (tool/invoke-tool {:tool-alias :deps, :fn 'clojure.tools.deps/resolve-added-libs, :args tool-args}) + ;_ (clojure.pprint/pprint _res) + paths (mapcat :paths (vals added)) + urls (->> paths (map jio/file) (map #(RT/toUrl ^File %)))] + (run! add-loader-url urls) + (basis-impl/update-basis! update :libs merge added) + ;; reload root *data-readers* from classpath + (set! *data-readers* (merge (#'clojure.core/load-data-readers) *data-readers*)) + (let [ret (-> added keys sort vec)] + (when (seq ret) ret)))))) + +(defn add-lib + "Given a lib that is not yet on the repl classpath, make it available by + downloading the library if necessary and adding it to the classloader. + Libs already on the classpath are not updated. Requires a valid parent + DynamicClassLoader. + + lib - symbol identifying a library, for Maven: groupId/artifactId + coord - optional map of location information specific to the procurer, + or latest if not supplied + + Returns coll of libs loaded, including transitive (or nil if none). + + For info on libs, coords, and versions, see: + https://clojure.org/reference/deps_and_cli" + {:added "1.12"} + ([lib coord] + (add-libs {lib coord})) + ([lib] + (let [procurer (select-keys (basis/current-basis) [:mvn/repos :mvn/local-repo]) + coord (tool/invoke-tool {:tool-alias :deps + :fn 'clojure.tools.deps/find-latest-version + :args {:lib lib, :procurer procurer}})] + (if coord + (add-libs {lib coord}) + (throw (ex-info (str "No version found for lib " lib) {})))))) + +(defn sync-deps + "Calls add-libs with any libs present in deps.edn but not yet present on the classpath. + + :aliases - coll of alias keywords to use during the sync" + {:added "1.12"} + [& {:as opts}] + (let [{:keys [aliases]} opts + basis-config (:basis-config (basis/current-basis)) + new-basis-config (update basis-config :aliases (fnil into []) aliases) + new-basis (tool/invoke-tool {:tool-alias :deps, :fn 'clojure.tools.deps/create-basis, :args new-basis-config}) + new-libs (:libs new-basis)] + (add-libs new-libs))) 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/tools/deps/interop.clj b/src/clj/clojure/tools/deps/interop.clj new file mode 100644 index 0000000000..0b6106801c --- /dev/null +++ b/src/clj/clojure/tools/deps/interop.clj @@ -0,0 +1,105 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. +(ns clojure.tools.deps.interop + "Functions for invoking Java processes and invoking tools via the Clojure CLI." + (:require + [clojure.java.process :as proc] + [clojure.edn :as edn] + [clojure.java.io :as jio] + [clojure.string :as str])) + +(set! *warn-on-reflection* true) + +(def ^:private build (atom nil)) + +(defn- cli-build + "Return CLI build number (a long) or nil if it can't be determined. + The build number is cached if found and subsequently read from cache." + [] + (or @build + (let [result (try + (proc/exec "clojure" "--version") + (catch Exception e "")) + ;; Version string: "Clojure CLI version 1.11.3.1463" + ;; Match MAJOR.MINOR.PATCH.BUILD and take a capture group just for the last BUILD part + version (-> (re-find #"[0-9]+\.[0-9]+\.[0-9]+\.([0-9]+)" result) (nth 1))] + (when version + (reset! build (parse-long version)))))) + +(defn- validate-version + [version] + (if version + (when (< version 1347) + (throw (RuntimeException. "Clojure CLI version is older than minimum required version, 1.11.1.1347. Please update to latest version."))) + (throw (ex-info "Clojure CLI version unknown, please install the latest version." {})))) + +(defn ^:dynamic invoke-tool + "Invoke tool using Clojure CLI. Args (one of :tool-alias or :tool-name, and :fn + are required): + :tool-alias - Tool alias to invoke (keyword) + :tool-name - Name of installed tool to invoke (string or symbol) + :fn - Function (symbol) + :args - map of args to pass to function + + Options: + :preserve-envelope - if true, return the full invocation envelope, default=false" + {:added "1.12"} + [{:keys [tool-name tool-alias fn args preserve-envelope] + :or {preserve-envelope false} + :as opts}] + (when-not (or tool-name tool-alias) (throw (ex-info "Either :tool-alias or :tool-name must be provided" (or opts {})))) + (when-not (symbol? fn) (throw (ex-info (str ":fn should be a symbol " fn) (or opts {})))) + (validate-version (cli-build)) + (let [args (conj [fn] (assoc args :clojure.exec/invoke :fn)) + _ (when (:debug opts) (println "args" args)) + command-strs ["clojure" (str "-T" (or tool-alias tool-name)) "-"] + _ (when (:debug opts) (apply println "Invoking: " command-strs)) + proc (apply proc/start command-strs) + in (proc/stdin proc) + out (proc/stdout proc) + err (proc/stderr proc)] + (binding [*print-length* nil + *print-level* nil + *print-namespace-maps* false] + (proc/io-task + #(with-open [w (jio/writer in)] + (doseq [a args] + (.write w (pr-str a)) + (.write w " "))))) + (if-let [envelope (edn/read-string (slurp out))] + (if preserve-envelope + envelope + (let [{:keys [tag val]} envelope + parsed-val (edn/read-string val)] + (if (= :ret tag) + parsed-val + (throw (ex-info (:cause parsed-val) (or parsed-val {})))))) + (let [err-str (slurp err) + err-msg (if (= "" err-str) "Unknown error invoking Clojure CLI" err-str)] + (throw (ex-info err-msg + {:command (str/join " " command-strs) + :in (str/join " " args)})))))) + +(comment + ;; regular invocation, should return {:hi :there} + (invoke-tool {:tool-alias :deps, :fn 'clojure.core/identity, :args {:hi :there}}) + + ;; invocation throws, should return throwable map data + (try + (invoke-tool {:tool-alias :deps, :fn 'clojure.core/+, :args {:fail :here}}) + (catch clojure.lang.ExceptionInfo e (ex-data e))) + + ;; capture stdout in returned envelope + (let [resp (invoke-tool {:tool-alias :deps, + :fn 'list + :args {:format :edn + :clojure.exec/out :capture} + :preserve-envelope true})] + (edn/read-string (:out resp))) + + ) diff --git a/src/clj/clojure/uuid.clj b/src/clj/clojure/uuid.clj index dbaa9d141e..4ffbdf60d5 100644 --- a/src/clj/clojure/uuid.clj +++ b/src/clj/clojure/uuid.clj @@ -9,8 +9,9 @@ (ns clojure.uuid) (defn- default-uuid-reader [form] - {:pre [(string? form)]} - (java.util.UUID/fromString form)) + (if (string? form) + (java.util.UUID/fromString form) + (throw (IllegalArgumentException. "#uuid data reader expected string")))) (defmethod print-method java.util.UUID [uuid ^java.io.Writer w] (.write w (str "#uuid \"" (str uuid) "\""))) diff --git a/src/clj/clojure/walk.clj b/src/clj/clojure/walk.clj index fe8fb631bd..0f027e7ad0 100644 --- a/src/clj/clojure/walk.clj +++ b/src/clj/clojure/walk.clj @@ -41,10 +41,10 @@ the sorting function."} {:added "1.1"} [inner outer form] (cond - (list? form) (outer (apply list (map inner form))) + (list? form) (outer (with-meta (apply list (map inner form)) (meta form))) (instance? clojure.lang.IMapEntry form) (outer (clojure.lang.MapEntry/create (inner (key form)) (inner (val form)))) - (seq? form) (outer (doall (map inner form))) + (seq? form) (outer (with-meta (doall (map inner form)) (meta form))) (instance? clojure.lang.IRecord form) (outer (reduce (fn [r x] (conj r (inner x))) form form)) (coll? form) (outer (into (empty form) (map inner form))) diff --git a/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/AFunction.java b/src/jvm/clojure/lang/AFunction.java
index 2ef5cd9b3a..4ab8c6932b 100644
--- a/src/jvm/clojure/lang/AFunction.java
+++ b/src/jvm/clojure/lang/AFunction.java
@@ -17,6 +17,8 @@
 
 public abstract class AFunction extends AFn implements IObj, Comparator, Fn, Serializable {
 
+private static final long serialVersionUID = 4469383498184457675L;
+
 public volatile MethodImplCache __methodImplCache;
 
 public IPersistentMap meta(){
diff --git a/src/jvm/clojure/lang/AMapEntry.java b/src/jvm/clojure/lang/AMapEntry.java
index 41ae7563a1..56debb4f1c 100644
--- a/src/jvm/clojure/lang/AMapEntry.java
+++ b/src/jvm/clojure/lang/AMapEntry.java
@@ -16,6 +16,8 @@
 
 public abstract class AMapEntry extends APersistentVector implements IMapEntry{
 
+private static final long serialVersionUID = -5007980429903443802L;
+
 public Object nth(int i){
 	if(i == 0)
 		return key();
diff --git a/src/jvm/clojure/lang/APersistentMap.java b/src/jvm/clojure/lang/APersistentMap.java
index 7f96271278..3dfb73a5ae 100644
--- a/src/jvm/clojure/lang/APersistentMap.java
+++ b/src/jvm/clojure/lang/APersistentMap.java
@@ -14,6 +14,9 @@
 import java.util.*;
 
 public abstract class APersistentMap extends AFn implements IPersistentMap, Map, Iterable, Serializable, MapEquivalence, IHashEq {
+
+private static final long serialVersionUID = 6736310834519110267L;
+
 int _hash;
 int _hasheq;
 
diff --git a/src/jvm/clojure/lang/APersistentSet.java b/src/jvm/clojure/lang/APersistentSet.java
index 1c2ce8f46c..2c8caad85b 100644
--- a/src/jvm/clojure/lang/APersistentSet.java
+++ b/src/jvm/clojure/lang/APersistentSet.java
@@ -18,6 +18,9 @@
 import java.util.Set;
 
 public abstract class APersistentSet extends AFn implements IPersistentSet, Collection, Set, Serializable, IHashEq {
+
+private static final long serialVersionUID = 889908853183699706L;
+
 int _hash;
 int _hasheq;
 final IPersistentMap impl;
diff --git a/src/jvm/clojure/lang/APersistentVector.java b/src/jvm/clojure/lang/APersistentVector.java
index c9f15cdd3f..20c40918eb 100644
--- a/src/jvm/clojure/lang/APersistentVector.java
+++ b/src/jvm/clojure/lang/APersistentVector.java
@@ -14,11 +14,15 @@
 
 import java.io.Serializable;
 import java.util.*;
+import java.util.function.Consumer;
 
 public abstract class APersistentVector extends AFn implements IPersistentVector, Iterable,
                                                                List,
                                                                RandomAccess, Comparable,
                                                                Serializable, IHashEq {
+
+private static final long serialVersionUID = 4667575149454420891L;
+
 int _hash;
 int _hasheq;
 
@@ -98,15 +102,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
 		{
@@ -270,6 +277,62 @@ public void remove(){
 	};
 }
 
+Spliterator rangedSpliterator(final int start, final int end) {
+	return new Spliterator() {
+		int i = start;
+
+		@Override
+		public int characteristics() {
+			return Spliterator.IMMUTABLE |   // persistent
+					Spliterator.ORDERED |    // know order
+					Spliterator.SIZED |      // know size
+					Spliterator.SUBSIZED;    // know size after split
+		}
+
+		@Override
+		public long estimateSize() {
+			return end-i;
+		}
+
+		@Override
+		public long getExactSizeIfKnown() {
+			return end-i;
+		}
+
+		@Override
+		public boolean tryAdvance(Consumer action) {
+			if(i < end) {
+				action.accept(nth(i++));
+				return true;
+			}
+			return false;
+		}
+
+		@Override
+		public Spliterator trySplit() {
+			int lo = i;
+			int mid = (lo + end) >>> 1; // avoid overflow
+			if(lo >= mid) {
+				return null;
+			} else {
+				i = mid;
+				return rangedSpliterator(lo, mid);
+			}
+		}
+
+		@Override
+		public void forEachRemaining(Consumer action) {
+			for(int x=i; x= end) || (i < 0))
 			throw new IndexOutOfBoundsException();
diff --git a/src/jvm/clojure/lang/ASeq.java b/src/jvm/clojure/lang/ASeq.java
index 325aa27c24..ec1b4d67f4 100644
--- a/src/jvm/clojure/lang/ASeq.java
+++ b/src/jvm/clojure/lang/ASeq.java
@@ -14,6 +14,9 @@
 import java.util.*;
 
 public abstract class ASeq extends Obj implements ISeq, Sequential, List, Serializable, IHashEq {
+
+private static final long serialVersionUID = 4748650717905139299L;
+
 transient int _hash;
 transient int _hasheq;
 
@@ -37,6 +40,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/ArityException.java b/src/jvm/clojure/lang/ArityException.java
index 49b7914b70..89d2c8f4fe 100644
--- a/src/jvm/clojure/lang/ArityException.java
+++ b/src/jvm/clojure/lang/ArityException.java
@@ -15,6 +15,8 @@
  */
 public class ArityException extends IllegalArgumentException {
 
+	private static final long serialVersionUID = 2265783180488869950L;
+
 	final public int actual;
 
 	final public String name;
@@ -24,7 +26,7 @@ public ArityException(int actual, String name) {
 	}
 
 	public ArityException(int actual, String name, Throwable cause) {
-		super("Wrong number of args (" + actual + ") passed to: " + Compiler.demunge(name), cause);
+		super("Wrong number of args (" + (actual <= 20 ? actual : "> 20") + ") passed to: " + Compiler.demunge(name), cause);
 		this.actual = actual;
 		this.name = name;
 	}
diff --git a/src/jvm/clojure/lang/ArrayChunk.java b/src/jvm/clojure/lang/ArrayChunk.java
index 97430839b9..ba6334cf54 100644
--- a/src/jvm/clojure/lang/ArrayChunk.java
+++ b/src/jvm/clojure/lang/ArrayChunk.java
@@ -16,6 +16,8 @@
 
 public final class ArrayChunk implements IChunk, Serializable {
 
+private static final long serialVersionUID = -8302142882294545702L;
+
 final Object[] array;
 final int off;
 final int end;
diff --git a/src/jvm/clojure/lang/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/BigInt.java b/src/jvm/clojure/lang/BigInt.java
index a3ca969410..1f2b7580d5 100644
--- a/src/jvm/clojure/lang/BigInt.java
+++ b/src/jvm/clojure/lang/BigInt.java
@@ -17,6 +17,8 @@
 
 public final class BigInt extends Number implements IHashEq{
 
+private static final long serialVersionUID = 5097771279236135022L;
+
 final public long lpart;
 final public BigInteger bipart;
 
diff --git a/src/jvm/clojure/lang/ChunkedCons.java b/src/jvm/clojure/lang/ChunkedCons.java
index b52bc2b62a..25b32200d3 100644
--- a/src/jvm/clojure/lang/ChunkedCons.java
+++ b/src/jvm/clojure/lang/ChunkedCons.java
@@ -14,6 +14,8 @@
 
 final public class ChunkedCons extends ASeq implements IChunkedSeq{
 
+private static final long serialVersionUID = 2773920188566401743L;
+
 final IChunk chunk;
 final ISeq _more;
 
diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java
index abac2251a2..4d58fcfd09 100644
--- a/src/jvm/clojure/lang/Compiler.java
+++ b/src/jvm/clojure/lang/Compiler.java
@@ -15,15 +15,20 @@
 //*
 
 import clojure.asm.*;
+import clojure.asm.Type;
 import clojure.asm.commons.GeneratorAdapter;
 import clojure.asm.commons.Method;
 
 import java.io.*;
+import java.lang.invoke.MethodType;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Modifier;
+import java.lang.reflect.Executable;
 import java.util.*;
+import java.util.concurrent.Callable;
 import java.util.regex.Pattern;
 import java.util.regex.Matcher;
+import java.util.stream.Collectors;
 
 //*/
 /*
@@ -206,13 +211,13 @@ public class Compiler implements Opcodes{
 static final public Var CONSTANT_IDS = Var.create().setDynamic();
 
 //vector
-static final public Var KEYWORD_CALLSITES = Var.create().setDynamic();
+static final public Var KEYWORD_CALLSITES = Var.create(null).setDynamic();
 
 //vector
-static final public Var PROTOCOL_CALLSITES = Var.create().setDynamic();
+static final public Var PROTOCOL_CALLSITES = Var.create(null).setDynamic();
 
 //set
-static final public Var VAR_CALLSITES = Var.create().setDynamic();
+//static final public Var VAR_CALLSITES = Var.create(null).setDynamic();
 
 //keyword->constid
 static final public Var KEYWORDS = Var.create().setDynamic();
@@ -387,7 +392,12 @@ static Symbol resolveSymbol(Symbol sym){
 		{
 		Namespace ns = namespaceFor(sym);
 		if(ns == null || (ns.name.name == null ? sym.ns == null : ns.name.name.equals(sym.ns)))
+			{
+			Class ac = HostExpr.maybeArrayClass(sym);
+			if(ac != null)
+				return Util.arrayTypeToSymbol(ac);
 			return sym;
+			}
 		return Symbol.intern(ns.name.name, sym.name);
 		}
 	Object o = currentNS().getMapping(sym);
@@ -410,7 +420,6 @@ static class DefExpr implements Expr{
 	public final Expr meta;
 	public final boolean initProvided;
 	public final boolean isDynamic;
-	public final boolean shadowsCoreMapping;
 	public final String source;
 	public final int line;
 	public final int column;
@@ -419,9 +428,8 @@ static class DefExpr implements Expr{
 	final static Method setMetaMethod = Method.getMethod("void setMeta(clojure.lang.IPersistentMap)");
 	final static Method setDynamicMethod = Method.getMethod("clojure.lang.Var setDynamic(boolean)");
 	final static Method symintern = Method.getMethod("clojure.lang.Symbol intern(String, String)");
-	final static Method internVar = Method.getMethod("clojure.lang.Var refer(clojure.lang.Symbol, clojure.lang.Var)");
 
-	public DefExpr(String source, int line, int column, Var var, Expr init, Expr meta, boolean initProvided, boolean isDynamic, boolean shadowsCoreMapping){
+	public DefExpr(String source, int line, int column, Var var, Expr init, Expr meta, boolean initProvided, boolean isDynamic){
 		this.source = source;
 		this.line = line;
 		this.column = column;
@@ -429,7 +437,6 @@ public DefExpr(String source, int line, int column, Var var, Expr init, Expr met
 		this.init = init;
 		this.meta = meta;
 		this.isDynamic = isDynamic;
-		this.shadowsCoreMapping = shadowsCoreMapping;
 		this.initProvided = initProvided;
 	}
 
@@ -475,18 +482,6 @@ public Object eval() {
 
 	public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
 		objx.emitVar(gen, var);
-
-		if (shadowsCoreMapping)
-		{
-			gen.dup();
-			gen.getField(VAR_TYPE, "ns", NS_TYPE);
-			gen.swap();
-			gen.dup();
-			gen.getField(VAR_TYPE, "sym", SYMBOL_TYPE);
-			gen.swap();
-			gen.invokeVirtual(NS_TYPE, internVar);
-		}
-
 		if(isDynamic)
 			{
 			gen.push(isDynamic);
@@ -544,13 +539,11 @@ else if(!(RT.second(form) instanceof Symbol))
 			Var v = lookupVar(sym, true);
 			if(v == null)
 				throw Util.runtimeException("Can't refer to qualified var that doesn't exist");
-			boolean shadowsCoreMapping = false;
 			if(!v.ns.equals(currentNS()))
 				{
 				if(sym.ns == null)
 					{
 					v = currentNS().intern(sym);
-					shadowsCoreMapping = true;
 					registerVar(v);
 					}
 //					throw Util.runtimeException("Name conflict, can't def " + sym + " because namespace: " + currentNS().name +
@@ -594,7 +587,7 @@ else if(!(RT.second(form) instanceof Symbol))
 			Expr meta = mm.count()==0 ? null:analyze(context == C.EVAL ? context : C.EXPRESSION, mm);
 			return new DefExpr((String) SOURCE.deref(), lineDeref(), columnDeref(),
 			                   v, analyze(context == C.EVAL ? context : C.EXPRESSION, RT.third(form), v.sym.name),
-			                   meta, RT.count(form) == 3, isDynamic, shadowsCoreMapping);
+			                   meta, RT.count(form) == 3, isDynamic);
 		}
 	}
 }
@@ -1021,7 +1014,7 @@ else if(instance != null && instance.hasJavaClass() && instance.getJavaClass() !
 				if(c != null)
 					return new StaticMethodExpr(source, line, column, tag, c, munge(sym.name), args, tailPosition);
 				else
-					return new InstanceMethodExpr(source, line, column, tag, instance, munge(sym.name), args, tailPosition);
+					return new InstanceMethodExpr(source, line, column, tag, instance, null, munge(sym.name), args, tailPosition);
 				}
 		}
 	}
@@ -1117,10 +1110,12 @@ static Class tagToClass(Object tag) {
         if(tag instanceof Symbol)
 			{
 			Symbol sym = (Symbol) tag;
-			if(sym.ns == null) //if ns-qualified can't be classname
+			if(sym.ns == null)
 				{
 				c = maybeSpecialTag(sym);
 				}
+			if(c == null)
+				c = HostExpr.maybeArrayClass(sym);
 			}
 		if(c == null)
 		    c = maybeClass(tag, true);
@@ -1128,6 +1123,297 @@ static Class tagToClass(Object tag) {
 			return c;
 		throw new IllegalArgumentException("Unable to resolve classname: " + tag);
 	}
+
+	public static boolean looksLikeArrayClass(Symbol sym) {
+		return sym.ns != null && Util.isPosDigit(sym.name);
+	}
+
+	public static String buildArrayClassDescriptor(Symbol sym) {
+		int dim = sym.name.charAt(0) - '0';
+		Symbol componentClassName = Symbol.intern(null, sym.ns);
+		Class componentClass = primClass(componentClassName);
+
+		if(componentClass == null)
+			componentClass = maybeClass(componentClassName, false);
+
+		if(componentClass == null)
+			throw Util.sneakyThrow(new ClassNotFoundException("Unable to resolve component classname: "
+					+ componentClassName));
+
+		StringBuilder arrayDescriptor = new StringBuilder();
+
+		for(int i=0; i hintedSig;
+	private final Symbol methodSymbol;
+	private final String methodName;
+	private final MethodKind kind;
+	private final Class tagClass;
+	private final StaticFieldExpr fieldOverload;
+
+	private enum MethodKind {
+		CTOR, INSTANCE, STATIC
+	}
+
+	public QualifiedMethodExpr(Class methodClass, Symbol sym) {
+		this(methodClass, sym, null);
+	}
+
+	public QualifiedMethodExpr(Class methodClass, Symbol sym, StaticFieldExpr fieldOL) {
+		c = methodClass;
+		methodSymbol = sym;
+		tagClass = tagOf(sym) != null ? HostExpr.tagToClass(tagOf(sym)) : AFn.class;
+		hintedSig = tagsToClasses(paramTagsOf(sym));
+		if(sym.name.startsWith(".")) {
+			kind = MethodKind.INSTANCE;
+			methodName = sym.name.substring(1);
+		}
+		else if(sym.name.equals("new")) {
+			kind = MethodKind.CTOR;
+			methodName = sym.name;
+		}
+		else {
+			kind = MethodKind.STATIC;
+			methodName = sym.name;
+		}
+		fieldOverload = fieldOL;
+	}
+
+	private boolean preferOverloadedField() {
+		return fieldOverload != null && paramTagsOf(methodSymbol) == null;
+	}
+
+	// Expr impl - invocation, convert to fn expr
+
+	@Override
+	public Object eval() {
+		if(preferOverloadedField())
+			return fieldOverload.eval();
+		else
+			return buildThunk(C.EVAL, this).eval();
+	}
+
+	@Override
+	public void emit(C context, ObjExpr objx, GeneratorAdapter gen) {
+		if(preferOverloadedField())
+			fieldOverload.emit(context, objx, gen);
+		else
+			buildThunk(context, this).emit(context, objx, gen);
+	}
+
+	// Expr impl - method value, always an AFn
+
+	@Override
+	public boolean hasJavaClass() {
+		return true;
+	}
+
+	@Override
+	public Class getJavaClass() {
+		return tagClass;
+	}
+
+	// TBD: caching/reuse of thunks
+	private static FnExpr buildThunk(C context, QualifiedMethodExpr qmexpr) {
+		// When qualified symbol has param-tags:
+		//   (fn invoke__Class_meth ([this? args*] (methodSymbol this? args*)))
+		// When no param-tags:
+		//   (fn invoke__Class_meth ([this?] (methodSymbol this?))
+		//                          ([this? arg1] (methodSymbol this? arg1)) ...)
+		IPersistentCollection form = PersistentVector.EMPTY;
+		Symbol instanceParam = qmexpr.kind == MethodKind.INSTANCE ? THIS : null;
+		String thunkName = "invoke__" + qmexpr.c.getSimpleName() + "_" + qmexpr.methodSymbol.name;
+		Set arities = (qmexpr.hintedSig != null) ? PersistentHashSet.create(qmexpr.hintedSig.size())
+				: aritySet(qmexpr.c, qmexpr.methodName, qmexpr.kind);
+
+		for(Object a : arities) {
+			int arity = (int) a;
+			IPersistentVector params = buildParams(instanceParam, arity);
+			ISeq body = RT.listStar(qmexpr.methodSymbol, params.seq());
+			form = RT.conj(form, RT.list(params, body));
+		}
+
+		ISeq thunkForm = RT.listStar(Symbol.intern("fn"), Symbol.intern(thunkName), RT.seq(form));
+		return (FnExpr) analyzeSeq(context, thunkForm, thunkName);
+	}
+
+	private static IPersistentVector buildParams(Symbol instanceParam, int arity) {
+		IPersistentVector params = PersistentVector.EMPTY;
+		if(instanceParam != null) params = params.cons(instanceParam);
+		for(int i = 0; i();
+		List methods = methodsWithName(c, methodName, kind);
+
+		for(Executable exec : methods)
+			res.add(exec.getParameterCount());
+
+		return res;
+	}
+
+	public static List methodOverloads(Class c, String methodName, MethodKind kind) {
+		final Executable[] methods = c.getMethods();
+		return Arrays.stream(methods)
+				.filter(m -> m.getName().equals(methodName))
+				.filter(m -> {
+					switch(kind) {
+						case STATIC: return isStaticMethod(m);
+						case INSTANCE: return isInstanceMethod(m);
+						default: return false;
+					}
+				})
+				.collect(Collectors.toList());
+	}
+
+	// Returns a list of methods or ctors matching the name and kind given.
+	// Otherwise, will throw if the information provided results in no matches
+	private static List methodsWithName(Class c, String methodName, MethodKind kind) {
+		if (kind == MethodKind.CTOR) {
+			List ctors = Arrays.asList(c.getConstructors());
+			if(ctors.isEmpty())
+				throw noMethodWithNameException(c, methodName, kind);
+			return ctors;
+		}
+
+		List res = methodOverloads(c, methodName, kind);
+
+		if(res.isEmpty())
+			throw noMethodWithNameException(c, methodName, kind);
+		return res;
+	}
+
+	static Executable resolveHintedMethod(Class c, String methodName, MethodKind kind, List hintedSig) {
+		List methods = methodsWithName(c, methodName, kind);
+		final int arity = hintedSig.size();
+		List filteredMethods = methods.stream()
+				.filter(m -> m.getParameterCount() == arity)
+				.filter(m -> !m.isSynthetic()) // remove bridge/lambda methods
+				.filter(m -> signatureMatches(hintedSig, m))
+				.collect(Collectors.toList());
+
+		if(filteredMethods.size() == 1)
+			return filteredMethods.get(0);
+		else
+			throw paramTagsDontResolveException(c, methodName, hintedSig);
+	}
+
+	static IllegalArgumentException noMethodWithNameException(Class c, String methodName, MethodKind kind) {
+		return new IllegalArgumentException("Error - no matches found for "
+				+ (kind != MethodKind.CTOR ? kind.toString().toLowerCase() + " " : "")
+				+ methodDescription(c, methodName));
+	}
+
+	static IllegalArgumentException paramTagsDontResolveException(Class c, String methodName, List hintedSig) {
+		IPersistentVector paramTags = PersistentVector.create(hintedSig.stream()
+				.map(tag -> tag == null ? PARAM_TAG_ANY : tag)
+				.collect(Collectors.toList()));
+		return new IllegalArgumentException("Error - param-tags " + paramTags
+				+ " insufficient to resolve "
+				+ methodDescription(c, methodName));
+	}
+
+	public static IllegalArgumentException instanceNoTargetException(QualifiedMethodExpr qmexpr) {
+		return new IllegalArgumentException(
+				"Malformed method expression, expecting (" +
+				qmexpr.c.getName() + "/." + qmexpr.methodName +
+				" target ...)");
+	}
+}
+
+final static Symbol PARAM_TAG_ANY = Symbol.intern(null, "_");
+
+private static IPersistentVector paramTagsOf(Symbol sym){
+	Object paramTags = RT.get(RT.meta(sym), RT.PARAM_TAGS_KEY);
+
+	if(paramTags != null && !(paramTags instanceof IPersistentVector))
+		throw new IllegalArgumentException("param-tags of symbol "
+				+ sym + " should be a vector.");
+
+	return (IPersistentVector) paramTags;
+}
+
+// calls tagToClass on every element, unless it encounters _ which becomes null
+private static List tagsToClasses(IPersistentVector paramTags) {
+	if(paramTags == null) return null;
+
+	List sig = new ArrayList<>();
+	for (ISeq s = RT.seq(paramTags); s!=null; s = s.next()) {
+		Object t = s.first();
+		if (t.equals(PARAM_TAG_ANY))
+			sig.add(null);
+		else
+			sig.add(HostExpr.tagToClass(t));
+	}
+	return sig;
+}
+
+private static boolean signatureMatches(List sig, Executable method)
+{
+	Class[] methodSig = method.getParameterTypes();
+	if(methodSig.length != sig.size()) return false;
+
+	for (int i = 0; i < methodSig.length; i++)
+		if (sig.get(i) != null && !sig.get(i).equals(methodSig[i]))
+			return false;
+
+	return true;
+};
+
+static boolean isStaticMethod(Executable method) {
+	return method instanceof java.lang.reflect.Method && Modifier.isStatic(method.getModifiers());
+}
+
+static boolean isInstanceMethod(Executable method) {
+	return method instanceof java.lang.reflect.Method && !Modifier.isStatic(method.getModifiers());
+}
+
+static boolean isConstructor(Executable method) {
+	return method instanceof Constructor;
+}
+
+private static void checkMethodArity(Executable method, int argCount) {
+        if(method.getParameterCount() != argCount)
+                throw new IllegalArgumentException("Invocation of "
+                                + methodDescription(method.getDeclaringClass(),
+                                (method instanceof Constructor) ? "new" : method.getName())
+                                + " expected " + method.getParameterCount() + " arguments, but received " + argCount);
+}
+
+private static String methodDescription(Class c, String methodName) {
+	boolean isCtor = c != null && methodName.equals("new");
+	String type = isCtor ? "constructor" : "method";
+	return type + (isCtor ? "" : " " + methodName) + " in class " + c.getName();
 }
 
 static abstract class FieldExpr extends HostExpr{
@@ -1364,6 +1650,161 @@ static Class maybePrimitiveType(Expr e){
 	return null;
 }
 
+static class FISupport {
+	private static final IPersistentSet AFN_FIS = RT.set(Callable.class, Runnable.class, Comparator.class);
+	private static final IPersistentSet OBJECT_METHODS = RT.set("equals", "toString", "hashCode");
+
+	// Return FI method if:
+	// 1) Target is a functional interface and not already implemented by AFn
+	// 2) Target method matches one of our fn invoker methods (0 <= arity <= 10)
+	static java.lang.reflect.Method maybeFIMethod(Class target) {
+		if (target != null && target.isAnnotationPresent(FunctionalInterface.class)
+				&& !AFN_FIS.contains(target)) {
+
+			java.lang.reflect.Method[] methods = target.getMethods();
+            for (java.lang.reflect.Method method : methods) {
+				if (method.getParameterCount() >= 0 && method.getParameterCount() <= 10
+						&& Modifier.isAbstract(method.getModifiers())
+						&& !OBJECT_METHODS.contains(method.getName()))
+					return method;
+			}
+		}
+		return null;
+	}
+
+	// Invokers support only long, double, Object params; widen numerics
+	private static Class toInvokerParamType(Class c) {
+		if (c.equals(Byte.TYPE) || c.equals(Short.TYPE) || c.equals(Integer.TYPE) || c.equals(Long.TYPE)) {
+			return Long.TYPE;
+		} else if (c.equals(Float.TYPE) || c.equals(Double.TYPE)) {
+			return Double.TYPE;
+		}
+		return Object.class;
+	}
+
+	/**
+	 * If targetClass is FI and has an adaptable functional method
+	 *   Find fn invoker method matching adaptable method of FI
+	 *   Emit bytecode for (expr is emitted):
+	 *     if(expr instanceof IFn && !(expr instanceof FI))
+	 *       invokeDynamic(targetMethod, fnInvokerImplMethod)
+	 * Else emit nothing
+	 */
+	static boolean maybeEmitFIAdapter(ObjExpr objx, GeneratorAdapter gen, Expr expr, Class targetClass) {
+		// Optimization:
+		// if(expr instanceof QualifiedMethodExpr)
+		//   emitInvokeDynamic(targetMethod, QME method) // DON'T emit expr
+
+		java.lang.reflect.Method targetMethod = maybeFIMethod(targetClass);
+		if (targetMethod == null)
+			return false;
+
+		// compute fn invoker method
+		int paramCount = targetMethod.getParameterCount();
+		Class[] invokerParams = new Class[paramCount + 1];
+		invokerParams[0] = IFn.class;  // close over Ifn as first arg
+		StringBuilder invokeMethodBuilder = new StringBuilder("invoke");
+		for (int i = 0; i < paramCount; i++) {
+			// FnInvokers only has prims for first 2 args
+			invokerParams[i + 1] = paramCount <= 2 ? toInvokerParamType(targetMethod.getParameterTypes()[i]) : Object.class;
+			invokeMethodBuilder.append(FnInvokers.encodeInvokerType(invokerParams[i + 1]));
+		}
+		// FnInvokers has prim returns for <= 2 params, only Object for higher
+		Class retType = targetMethod.getReturnType();
+		char invokerReturnCode = FnInvokers.encodeInvokerType(paramCount <= 2 ? retType : Object.class);
+		invokeMethodBuilder.append(invokerReturnCode);
+		String invokerMethodName = invokeMethodBuilder.toString();
+
+		// Emit adapter to fn invoker method
+		Type samType = Type.getType(targetClass);
+		Type ifnType = Type.getType(IFn.class);
+		try {
+			java.lang.reflect.Method fnInvokerMethod = FnInvokers.class.getMethod(invokerMethodName, invokerParams);
+
+			// if not (expr instanceof IFn), go to end label
+			expr.emit(C.EXPRESSION, objx, gen);
+			gen.dup();
+			gen.instanceOf(ifnType);
+			Label endLabel = gen.newLabel();
+			gen.ifZCmp(Opcodes.IFEQ, endLabel);
+
+			// if (expr instanceof FI), go to end label
+			gen.dup();
+			gen.instanceOf(samType);
+			gen.ifZCmp(Opcodes.IFNE, endLabel);
+
+			// else adapt fn invoker method as impl for target method
+			emitInvokeDynamicAdapter(gen, targetClass, targetMethod, FnInvokers.class, fnInvokerMethod);
+
+			// end - checkcast that we have the target FI type
+			gen.mark(endLabel);
+			gen.checkCast(samType);
+			return true;
+		} catch (NoSuchMethodException e) {
+			throw Util.sneakyThrow(e); // should never happen
+		}
+
+	}
+
+	// LambdaMetafactory.metafactory() method handle for lambda bootstrap
+	private static final Handle LMF_HANDLE =
+			new Handle(Opcodes.H_INVOKESTATIC,
+					"java/lang/invoke/LambdaMetafactory",
+					"metafactory",
+					"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;",
+					false);
+
+	/**
+	 * Emit an invokedynamic to adapt an implMethod to act as a targetMethod.
+	 *
+	 * implMethod may be a static method, a constructor, or an instance method. If it is an
+	 * instance method, the first argument is the invocation instance.
+	 *
+	 * The implMethod may close over objects on the stack - these are passed as the initial arguments
+	 * to implMethod. The trailing arguments must match the targetMethod arguments.
+	 *
+	 * See: https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/LambdaMetafactory.html
+	 * @param gen          ASM code generator, expects any closed-overs to be on the stack already
+	 * @param targetClass  The target class
+	 * @param targetMethod The target method
+	 * @param implClass    The impl class
+	 * @param implMethod   The impl method that will be adapted, takes closed-overs + args of targetMethod
+	 */
+	static void emitInvokeDynamicAdapter(GeneratorAdapter gen,
+												 Class targetClass, java.lang.reflect.Method targetMethod,
+												 Class implClass, Executable implMethod) {
+
+		// Impl method - takes closed overs (on stack now) + args (when called)
+		Class[] implParams = implMethod.getParameterTypes();
+		Class retClass = isConstructor(implMethod) ? implClass
+				: ((java.lang.reflect.Method) implMethod).getReturnType();
+
+		int opCode = isConstructor(implMethod) ? Opcodes.H_INVOKESPECIAL :
+				(isStaticMethod(implMethod) ? Opcodes.H_INVOKESTATIC :
+						Opcodes.H_INVOKEVIRTUAL);
+
+		Handle implHandle = new Handle(opCode,
+				Type.getInternalName(implClass),
+				implMethod.getName(),
+				MethodType.methodType(retClass, implParams).toMethodDescriptorString(),
+				false);
+
+		// Adapter interface lambda-style: (closedOver*) -> targetType
+		int implArgCount = implParams.length;
+		if (isInstanceMethod(implMethod))  // instance is first "arg"
+			implArgCount++;
+		List lambdaParams = Arrays.asList(Arrays.copyOfRange(implParams, 0, implArgCount - targetMethod.getParameterCount()));
+		MethodType lambdaSig = MethodType.methodType(targetClass, lambdaParams);
+
+		Type targetType = Type.getType(targetMethod);
+		gen.visitInvokeDynamicInsn(
+				targetMethod.getName(),
+				lambdaSig.toMethodDescriptorString(),  // adapter signature, closedOvers -> target
+				LMF_HANDLE,  // bootstrap method handle: LambdaMetaFactory.metafactory()
+				new Object[]{targetType, implHandle, targetType}); // arg types of bootstrap method
+	}
+}
+
 static Class maybeJavaClass(Collection exprs){
     Class match = null;
     try
@@ -1375,6 +1816,8 @@ static Class maybeJavaClass(Collection exprs){
         if (!e.hasJavaClass())
             return null;
         Class c = e.getJavaClass();
+        if (c == null)
+            return null;
         if (match == null)
             match = c;
         else if (match != c)
@@ -1441,7 +1884,7 @@ else if(primc == double.class && parameterTypes[i] == float.class)
 					pe.emitUnboxed(C.EXPRESSION, objx, gen);
 					gen.visitInsn(D2F);
 					}
-				else
+				else if(!FISupport.maybeEmitFIAdapter(objx, gen, e, parameterTypes[i]))
 					{
 					e.emit(C.EXPRESSION, objx, gen);
 					HostExpr.emitUnboxArg(objx, gen, parameterTypes[i]);
@@ -1466,14 +1909,34 @@ static class InstanceMethodExpr extends MethodExpr{
 	public final Symbol tag;
 	public final boolean tailPosition;
 	public final java.lang.reflect.Method method;
+	public final Class qualifyingClass;
     Class jc;
 
 	final static Method invokeInstanceMethodMethod =
 			Method.getMethod("Object invokeInstanceMethod(Object,String,Object[])");
+	final static Method invokeInstanceMethodOfClassMethod =
+			Method.getMethod("Object invokeInstanceMethodOfClass(Object,String,String,Object[])");
 
+	public InstanceMethodExpr(String source, int line, int column, Symbol tag, Expr target,
+			Class qualifyingClass, String methodName, java.lang.reflect.Method resolvedMethod,
+			IPersistentVector args, boolean tailPosition)
+	{
+		checkMethodArity(resolvedMethod, RT.count(args));
+
+		this.source = source;
+		this.line = line;
+		this.column = column;
+		this.args = args;
+		this.methodName = methodName;
+		this.target = target;
+		this.tag = tag;
+		this.tailPosition = tailPosition;
+		this.method = resolvedMethod;
+		this.qualifyingClass = qualifyingClass;
+	}
 
 	public InstanceMethodExpr(String source, int line, int column, Symbol tag, Expr target,
-			String methodName, IPersistentVector args, boolean tailPosition)
+			Class qualifyingClass, String methodName, IPersistentVector args, boolean tailPosition)
 			{
 		this.source = source;
 		this.line = line;
@@ -1483,9 +1946,12 @@ public InstanceMethodExpr(String source, int line, int column, Symbol tag, Expr
 		this.target = target;
 		this.tag = tag;
 		this.tailPosition = tailPosition;
-		if(target.hasJavaClass() && target.getJavaClass() != null)
+		this.qualifyingClass = qualifyingClass;
+		Class contextClass = qualifyingClass != null ? qualifyingClass :
+				(target.hasJavaClass() ? target.getJavaClass() : null);
+		if(contextClass != null)
 			{
-			List methods = Reflector.getMethods(target.getJavaClass(), args.count(), methodName, false);
+			List methods = Reflector.getMethods(contextClass, args.count(), methodName, false);
 			if(methods.isEmpty())
 				{
 				method = null;
@@ -1493,7 +1959,7 @@ public InstanceMethodExpr(String source, int line, int column, Symbol tag, Expr
 					{
 					RT.errPrintWriter()
 						.format("Reflection warning, %s:%d:%d - call to method %s on %s can't be resolved (no such method).\n",
-							SOURCE_PATH.deref(), line, column, methodName, target.getJavaClass().getName());
+							SOURCE_PATH.deref(), line, column, methodName, contextClass.getName());
 					}
 				}
 			else
@@ -1523,7 +1989,7 @@ public InstanceMethodExpr(String source, int line, int column, Symbol tag, Expr
 					{
 					RT.errPrintWriter()
 						.format("Reflection warning, %s:%d:%d - call to method %s on %s can't be resolved (argument types: %s).\n",
-							SOURCE_PATH.deref(), line, column, methodName, target.getJavaClass().getName(), getTypeStringForArgs(args));
+							SOURCE_PATH.deref(), line, column, methodName, contextClass.getName(), getTypeStringForArgs(args));
 					}
 				}
 			}
@@ -1552,7 +2018,10 @@ public Object eval() {
 				ms.add(method);
 				return Reflector.invokeMatchingMethod(methodName, ms, targetval, argvals);
 				}
-			return Reflector.invokeInstanceMethod(targetval, methodName, argvals);
+			if(qualifyingClass != null)
+				return Reflector.invokeInstanceMethodOfClass(targetval, qualifyingClass, methodName, argvals);
+			else
+				return Reflector.invokeInstanceMethod(targetval, methodName, argvals);
 			}
 		catch(Throwable e)
 			{
@@ -1610,12 +2079,22 @@ 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
 			{
 			target.emit(C.EXPRESSION, objx, gen);
+			if(qualifyingClass != null)
+				gen.push(qualifyingClass.getName());
 			gen.push(methodName);
 			emitArgsAsArray(args, objx, gen);
 			gen.visitLineNumber(line, gen.mark());
@@ -1624,10 +2103,13 @@ public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
 				ObjMethod method = (ObjMethod) METHOD.deref();
 				method.emitClearLocals(gen);
 				}
-			gen.invokeStatic(REFLECTOR_TYPE, invokeInstanceMethodMethod);
+			if(qualifyingClass != null)
+				gen.invokeStatic(REFLECTOR_TYPE, invokeInstanceMethodOfClassMethod);
+			else
+				gen.invokeStatic(REFLECTOR_TYPE, invokeInstanceMethodMethod);
+			if(context == C.STATEMENT)
+				gen.pop();
 			}
-		if(context == C.STATEMENT)
-			gen.pop();
 	}
 
 	public boolean hasJavaClass(){
@@ -1659,6 +2141,29 @@ static class StaticMethodExpr extends MethodExpr{
 	final static Keyword warnOnBoxedKeyword = Keyword.intern("warn-on-boxed");
     Class jc;
 
+	public StaticMethodExpr(String source, int line, int column, Symbol tag, Class c,
+							String methodName, java.lang.reflect.Method preferredMethod, IPersistentVector args, boolean tailPosition)
+	{
+		checkMethodArity(preferredMethod, RT.count(args));
+
+		this.c = c;
+		this.methodName = methodName;
+		this.args = args;
+		this.source = source;
+		this.line = line;
+		this.column = column;
+		this.tag = tag;
+		this.tailPosition = tailPosition;
+		this.method = preferredMethod;
+
+		if(method != null && warnOnBoxedKeyword.equals(RT.UNCHECKED_MATH.deref()) && isBoxedMath(method))
+		{
+			RT.errPrintWriter()
+					.format("Boxed math warning, %s:%d:%d - call: %s.\n",
+							SOURCE_PATH.deref(), line, column, method.toString());
+		}
+	}
+
 	public StaticMethodExpr(String source, int line, int column, Symbol tag, Class c,
 				String methodName, IPersistentVector args, boolean tailPosition)
 			{
@@ -2020,7 +2525,9 @@ else if(v == Boolean.FALSE)
 				return NumberExpr.parse((Number)v);
 			else if(v instanceof String)
 				return new StringExpr((String) v);
-			else if(v instanceof IPersistentCollection && ((IPersistentCollection) v).count() == 0)
+			else if(v instanceof IPersistentCollection
+					&& (((IPersistentCollection) v).count() == 0)
+					&& (!(v instanceof IObj) || ((IObj) v).meta() == null))
 				return new EmptyExpr(v);
 			else
 				return new ConstantExpr(v);
@@ -2568,6 +3075,13 @@ public static class NewExpr implements Expr{
 			Method.getMethod("Object invokeConstructor(Class,Object[])");
 	final static Method forNameMethod = Method.getMethod("Class classForName(String)");
 
+	public NewExpr(Class c, Constructor preferredConstructor, IPersistentVector args, int line, int column) {
+		checkMethodArity(preferredConstructor, RT.count(args));
+
+		this.args = args;
+		this.c = c;
+		this.ctor = preferredConstructor;
+	}
 
 	public NewExpr(Class c, IPersistentVector args, int line, int column) {
 		this.args = args;
@@ -3639,7 +4153,13 @@ static Object sigTag(int argcount, Var v){
                 return tagOf(sig);
             }
         return null;
-        }
+	}
+
+	// Callsites are only registered in a function context
+	// In KEYWORD/PROTOCOL_CALLSITES, null indicates "do not register"
+	static boolean shouldRegisterCallsites(Var callSiteVar) {
+		return callSiteVar.deref() != null;
+	}
 
 	public InvokeExpr(String source, int line, int column, Symbol tag, Expr fexpr, IPersistentVector args, boolean tailPosition) {
 		this.source = source;
@@ -3653,7 +4173,7 @@ public InvokeExpr(String source, int line, int column, Symbol tag, Expr fexpr, I
 			{
 			Var fvar = ((VarExpr)fexpr).var;
 			Var pvar =  (Var)RT.get(fvar.meta(), protocolKey);
-			if(pvar != null && PROTOCOL_CALLSITES.isBound())
+			if(pvar != null && shouldRegisterCallsites(PROTOCOL_CALLSITES))
 				{
 				this.isProtocol = true;
 				this.siteIndex = registerProtocolCallsite(((VarExpr)fexpr).var);
@@ -3875,24 +4395,78 @@ static public Expr parse(C context, ISeq form) {
 				}
 			}
 
-		if(fexpr instanceof KeywordExpr && RT.count(form) == 2 && KEYWORD_CALLSITES.isBound())
+		if(fexpr instanceof KeywordExpr && RT.count(form) == 2 && shouldRegisterCallsites(KEYWORD_CALLSITES))
 			{
 //			fexpr = new ConstantExpr(new KeywordCallSite(((KeywordExpr)fexpr).k));
 			Expr target = analyze(context, RT.second(form));
 			return new KeywordInvokeExpr((String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form),
 			                             (KeywordExpr) fexpr, target);
 			}
+
 		PersistentVector args = PersistentVector.EMPTY;
 		for(ISeq s = RT.seq(form.next()); s != null; s = s.next())
 			{
 			args = args.cons(analyze(context, s.first()));
 			}
+
+		// Preserving the existing static field syntax that replaces a reference in parens with
+		// the field itself rather than trying to invoke the value in the field. This is
+		// an exception to the uniform Class/member qualification per CLJ-2806 ticket.
+		if(fexpr instanceof StaticFieldExpr)
+		{
+			if(RT.count(args) == 0)
+				return fexpr;
+			else
+				throw new IllegalArgumentException("No matching method " +
+						((StaticFieldExpr) fexpr).fieldName +
+						" found taking " + RT.count(args) + " args for " +
+						((StaticFieldExpr) fexpr).c);
+		}
+
+		if(fexpr instanceof QualifiedMethodExpr)
+		{
+			QualifiedMethodExpr qmexpr = (QualifiedMethodExpr)fexpr;
+			if (qmexpr.kind == QualifiedMethodExpr.MethodKind.INSTANCE && RT.count(args) == 0)
+				throw QualifiedMethodExpr.instanceNoTargetException(qmexpr);
+			return toHostExpr(qmexpr, (String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form), tailPosition, args);
+		}
+
 //		if(args.count() > MAX_POSITIONAL_ARITY)
 //			throw new IllegalArgumentException(
 //					String.format("No more than %d args supported", MAX_POSITIONAL_ARITY));
 
 		return new InvokeExpr((String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form), fexpr, args, tailPosition);
 	}
+
+	private static Expr toHostExpr(QualifiedMethodExpr qmexpr, String source, int line, int column, Symbol tag, boolean tailPosition, IPersistentVector args) {
+		if(qmexpr.hintedSig != null) {
+			Executable method = QualifiedMethodExpr.resolveHintedMethod(qmexpr.c, qmexpr.methodName, qmexpr.kind, qmexpr.hintedSig);
+			switch(qmexpr.kind) {
+				case CTOR:
+					return new NewExpr(qmexpr.c, (Constructor) method, args, line, column);
+				case INSTANCE:
+					return new InstanceMethodExpr(source, line, column, tag, (Expr) RT.first(args),
+							qmexpr.c, munge(qmexpr.methodName), (java.lang.reflect.Method) method,
+							PersistentVector.create(RT.next(args)),
+							tailPosition);
+				default:
+					return new StaticMethodExpr(source, line, column, tag, qmexpr.c,
+							munge(qmexpr.methodName), (java.lang.reflect.Method) method, args, tailPosition);
+			}
+		}
+		else {
+			switch(qmexpr.kind) {
+				case CTOR:
+					return new NewExpr(qmexpr.c, args, line, column);
+				case INSTANCE:
+					return new InstanceMethodExpr(source, line, column, tag, (Expr) RT.first(args), qmexpr.c,
+							munge(qmexpr.methodName), PersistentVector.create(RT.next(args)), tailPosition);
+				default:
+					return new StaticMethodExpr(source, line, column, tag, qmexpr.c,
+							munge(qmexpr.methodName), args, tailPosition);
+			}
+		}
+	}
 }
 
 static class SourceDebugExtensionAttribute extends Attribute{
@@ -4004,7 +4578,7 @@ CONSTANT_IDS, new IdentityHashMap(),
 					       VARS, PersistentHashMap.EMPTY,
 					       KEYWORD_CALLSITES, PersistentVector.EMPTY,
 					       PROTOCOL_CALLSITES, PersistentVector.EMPTY,
-					       VAR_CALLSITES, emptyVarCallSites(),
+					       //VAR_CALLSITES, emptyVarCallSites(),
                                                NO_RECUR, null
 					));
 
@@ -4084,7 +4658,7 @@ else if(methodArray[f.reqParms.count()] == null)
 			fn.constants = (PersistentVector) CONSTANTS.deref();
 			fn.keywordCallsites = (IPersistentVector) KEYWORD_CALLSITES.deref();
 			fn.protocolCallsites = (IPersistentVector) PROTOCOL_CALLSITES.deref();
-			fn.varCallsites = (IPersistentSet) VAR_CALLSITES.deref();
+			//fn.varCallsites = (IPersistentSet) VAR_CALLSITES.deref();
 
 			fn.constantsID = RT.nextID();
 //			DynamicClassLoader loader = (DynamicClassLoader) LOADER.get();
@@ -4189,7 +4763,7 @@ static public class ObjExpr implements Expr{
 
 	IPersistentVector keywordCallsites;
 	IPersistentVector protocolCallsites;
-	IPersistentSet varCallsites;
+	//IPersistentSet varCallsites;
 	boolean onceOnly = false;
 
 	Object src;
@@ -4993,7 +5567,7 @@ public Object eval() {
 			return null;
 		try
 			{
-			return getCompiledClass().newInstance();
+			return getCompiledClass().getDeclaredConstructor().newInstance();
 			}
 		catch(Exception e)
 			{
@@ -5233,9 +5807,9 @@ String cachedVarName(int n){
 		return "__cached_var__" + n;
 	}
 
-	String varCallsiteName(int n){
-		return "__var__callsite__" + n;
-	}
+	//String varCallsiteName(int n){
+	//	return "__var__callsite__" + n;
+	//}
 
 	String thunkNameStatic(int n){
 		return thunkName(n) + "__";
@@ -5343,9 +5917,8 @@ static FnMethod parse(ObjExpr objx, ISeq form, Object rettag) {
 			method.line = lineDeref();
 			method.column = columnDeref();
 			//register as the current method and set up a new env frame
-            PathNode pnode =  (PathNode) CLEAR_PATH.get();
-			if(pnode == null)
-				pnode = new PathNode(PATHTYPE.PATH,null);
+            PathNode pnode = new PathNode(PATHTYPE.PATH,null);
+			method.clearRoot = pnode;
 			Var.pushThreadBindings(
 					RT.mapUniqueKeys(
 							METHOD, method,
@@ -5789,6 +6362,7 @@ abstract public static class ObjMethod{
 	boolean usesThis = false;
 	PersistentHashSet localsUsedInCatchFinally = PersistentHashSet.EMPTY;
 	protected IPersistentMap methodMeta;
+	PathNode clearRoot;
 
 
 	public final IPersistentMap locals(){
@@ -6012,9 +6586,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();
@@ -6038,7 +6617,9 @@ public LocalBindingExpr(LocalBinding b, Symbol tag)
                     }
                 }
 
-            if(clearRoot == b.clearPathRoot)
+            ObjMethod method = ((ObjMethod) METHOD.deref());
+            boolean closedOver = method.objx.closes.containsKey(b);
+            if(clearRoot == b.clearPathRoot || (closedOver && clearRoot == method.clearRoot))
                 {
                 this.shouldClear = true;
                 sites = RT.conj(sites,this);
@@ -6487,7 +7068,10 @@ public void doEmit(C context, ObjExpr objx, GeneratorAdapter gen, boolean emitUn
 				}
 			else
 				{
-				bi.init.emit(C.EXPRESSION, objx, gen);
+				Class bindingClass = HostExpr.maybeClass(bi.binding.tag, true);
+				if(!FISupport.maybeEmitFIAdapter(objx, gen, bi.init, bindingClass))
+					bi.init.emit(C.EXPRESSION, objx, gen);
+
 				if (!bi.binding.used && bi.binding.canBeCleared)
 					gen.pop();
 				else
@@ -6656,6 +7240,11 @@ public Expr parse(C context, Object frm) {
 			int column = columnDeref();
 			String source = (String) SOURCE.deref();
 
+			// In :once fn, recur to head invalidates :once
+			ObjMethod method = (ObjMethod)METHOD.deref();
+			if(method.objx.onceOnly && method.clearRoot == CLEAR_ROOT.deref())
+				method.objx.onceOnly = false;
+
 			ISeq form = (ISeq) frm;
 			IPersistentVector loopLocals = (IPersistentVector) LOOP_LOCALS.deref();
 			if(context != C.RETURN || loopLocals == null)
@@ -7026,7 +7615,8 @@ public static Object macroexpand1(Object x) {
 				Symbol sym = (Symbol) op;
 				String sname = sym.name;
 				//(.substring s 2 5) => (. s substring 2 5)
-				if(sym.name.charAt(0) == '.')
+				// ns == null ensures that Class/.instanceMethod isn't expanded to . form
+				if(sym.name.charAt(0) == '.' && sym.ns == null)
 					{
 					if(RT.length(form) < 2)
 						throw new IllegalArgumentException(
@@ -7039,16 +7629,6 @@ public static Object macroexpand1(Object x) {
 						}
 					return preserveTag(form, RT.listStar(DOT, target, meth, form.next().next()));
 					}
-				else if(namesStaticMember(sym))
-					{
-					Symbol target = Symbol.intern(sym.ns);
-					Class c = HostExpr.maybeClass(target, false);
-					if(c != null)
-						{
-						Symbol meth = Symbol.intern(sym.name);
-						return preserveTag(form, RT.listStar(DOT, target, meth, form.next()));
-						}
-					}
 				else
 					{
 					//(s.substring 2 5) => (. s substring 2 5)
@@ -7226,9 +7806,6 @@ private static KeywordExpr registerKeyword(Keyword keyword){
 }
 
 private static int registerKeywordCallsite(Keyword keyword){
-	if(!KEYWORD_CALLSITES.isBound())
-		throw new IllegalAccessError("KEYWORD_CALLSITES is not bound");
-
 	IPersistentVector keywordCallsites = (IPersistentVector) KEYWORD_CALLSITES.deref();
 
 	keywordCallsites = keywordCallsites.cons(keyword);
@@ -7237,9 +7814,6 @@ private static int registerKeywordCallsite(Keyword keyword){
 }
 
 private static int registerProtocolCallsite(Var v){
-	if(!PROTOCOL_CALLSITES.isBound())
-		throw new IllegalAccessError("PROTOCOL_CALLSITES is not bound");
-
 	IPersistentVector protocolCallsites = (IPersistentVector) PROTOCOL_CALLSITES.deref();
 
 	protocolCallsites = protocolCallsites.cons(v);
@@ -7247,16 +7821,16 @@ private static int registerProtocolCallsite(Var v){
 	return protocolCallsites.count()-1;
 }
 
-private static void registerVarCallsite(Var v){
-	if(!VAR_CALLSITES.isBound())
-		throw new IllegalAccessError("VAR_CALLSITES is not bound");
-
-	IPersistentCollection varCallsites = (IPersistentCollection) VAR_CALLSITES.deref();
-
-	varCallsites = varCallsites.cons(v);
-	VAR_CALLSITES.set(varCallsites);
-//	return varCallsites.count()-1;
-}
+//private static void registerVarCallsite(Var v){
+//	if(!VAR_CALLSITES.isBound())
+//		throw new IllegalAccessError("VAR_CALLSITES is not bound");
+//
+//	IPersistentCollection varCallsites = (IPersistentCollection) VAR_CALLSITES.deref();
+//
+//	varCallsites = varCallsites.cons(v);
+//	VAR_CALLSITES.set(varCallsites);
+////	return varCallsites.count()-1;
+//}
 
 static ISeq fwdPath(PathNode p1){
     ISeq ret = null;
@@ -7300,16 +7874,25 @@ private static Expr analyzeSymbol(Symbol sym) {
 		}
 	else
 		{
-		if(namespaceFor(sym) == null)
+		if(namespaceFor(sym) == null && !Util.isPosDigit(sym.name))
 			{
 			Symbol nsSym = Symbol.intern(sym.ns);
 			Class c = HostExpr.maybeClass(nsSym, false);
 			if(c != null)
 				{
 				if(Reflector.getField(c, sym.name, true) != null)
-					return new StaticFieldExpr(lineDeref(), columnDeref(), c, sym.name, tag);
-				throw Util.runtimeException("Unable to find static field: " + sym.name + " in " + c);
+					{
+					List maybeOverloads = QualifiedMethodExpr.methodOverloads(c, sym.name, QualifiedMethodExpr.MethodKind.STATIC);
+
+					if(maybeOverloads.isEmpty())
+						return new StaticFieldExpr(lineDeref(), columnDeref(), c, sym.name, tag);
+					else
+						return new QualifiedMethodExpr(c, sym, new StaticFieldExpr(lineDeref(), columnDeref(), c, sym.name, tag));
+					}
+				else
+					return new QualifiedMethodExpr(c, sym);
 				}
+
 			}
 		}
 	//Var v = lookupVar(sym, false);
@@ -7381,7 +7964,12 @@ static public Object resolveIn(Namespace n, Symbol sym, boolean allowPrivate) {
 		{
 		Namespace ns = namespaceFor(n, sym);
 		if(ns == null)
+			{
+			Class ac = HostExpr.maybeArrayClass(sym);
+			if(ac != null)
+				return ac;
 			throw Util.runtimeException("No such namespace: " + sym.ns);
+			}
 
 		Var v = ns.findInternedVar(Symbol.intern(sym.name));
 		if(v == null)
@@ -7425,7 +8013,7 @@ static public Object maybeResolveIn(Namespace n, Symbol sym) {
 		{
 		Namespace ns = namespaceFor(n, sym);
 		if(ns == null)
-			return null;
+			return HostExpr.maybeArrayClass(sym);
 		Var v = ns.findInternedVar(Symbol.intern(sym.name));
 		if(v == null)
 			return null;
@@ -7539,7 +8127,6 @@ static void closeOver(LocalBinding b, ObjMethod method){
 		}
 }
 
-
 static LocalBinding referenceLocal(Symbol sym) {
 	if(!LOCAL_ENV.isBound())
 		return null;
@@ -7645,7 +8232,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;
 		}
@@ -7761,6 +8348,9 @@ public static Object compile(Reader rdr, String sourcePath, String sourceName) t
 			       COLUMN_AFTER, pushbackReader.getColumnNumber(),
 			       CONSTANTS, PersistentVector.EMPTY,
 			       CONSTANT_IDS, new IdentityHashMap(),
+			       KEYWORD_CALLSITES, null,
+			       PROTOCOL_CALLSITES, null,
+			       //VAR_CALLSITES, null,
 			       KEYWORDS, PersistentHashMap.EMPTY,
 			       VARS, PersistentHashMap.EMPTY
 					,RT.UNCHECKED_MATH, RT.UNCHECKED_MATH.deref()
@@ -7958,9 +8548,11 @@ public Expr parse(C context, Object frm) {
 
 
 		ObjExpr ret = build(interfaces, null, null, classname, Symbol.intern(classname), null, rform, frm, null);
-		if(frm instanceof IObj && ((IObj) frm).meta() != null)
-			return new MetaExpr(ret, MapExpr
-					.parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) frm).meta()));
+		IPersistentMap fmeta = RT.meta(frm);
+		if(fmeta != null)
+			fmeta = fmeta.without(RT.LINE_KEY).without(RT.COLUMN_KEY).without(RT.FILE_KEY);
+		if (RT.count(fmeta) > 0)
+			return new MetaExpr(ret, MapExpr.parse(context == C.EVAL ? context : C.EXPRESSION, fmeta));
 		else
 			return ret;
 	}
@@ -8038,7 +8630,7 @@ CONSTANT_IDS, new IdentityHashMap(),
 					       VARS, PersistentHashMap.EMPTY,
 					       KEYWORD_CALLSITES, PersistentVector.EMPTY,
 					       PROTOCOL_CALLSITES, PersistentVector.EMPTY,
-					       VAR_CALLSITES, emptyVarCallSites(),
+					       //VAR_CALLSITES, emptyVarCallSites(),
                                                NO_RECUR, null));
 			if(ret.isDeftype())
 				{
@@ -8068,7 +8660,7 @@ VAR_CALLSITES, emptyVarCallSites(),
 			ret.constantsID = RT.nextID();
 			ret.keywordCallsites = (IPersistentVector) KEYWORD_CALLSITES.deref();
 			ret.protocolCallsites = (IPersistentVector) PROTOCOL_CALLSITES.deref();
-			ret.varCallsites = (IPersistentSet) VAR_CALLSITES.deref();
+			//ret.varCallsites = (IPersistentSet) VAR_CALLSITES.deref();
 			}
 		finally
 			{
@@ -8444,7 +9036,8 @@ static NewInstanceMethod parse(ObjExpr objx, ISeq form, Symbol thistag,
 			method.line = lineDeref();
 			method.column = columnDeref();
 			//register as the current method and set up a new env frame
-            PathNode pnode =  new PathNode(PATHTYPE.PATH, (PathNode) CLEAR_PATH.get());
+            PathNode pnode = new PathNode(PATHTYPE.PATH, null);
+			method.clearRoot = pnode;
 			Var.pushThreadBindings(
 					RT.mapUniqueKeys(
 							METHOD, method,
@@ -8654,7 +9247,7 @@ static Class retType(Class tc, Class ret){
         return tc;
     }
 
-	static Class primClass(Symbol sym){
+	public static Class primClass(Symbol sym){
 		if(sym == null)
 			return null;
 		Class c = null;
diff --git a/src/jvm/clojure/lang/Cons.java b/src/jvm/clojure/lang/Cons.java
index 88dceef395..9378881ddc 100644
--- a/src/jvm/clojure/lang/Cons.java
+++ b/src/jvm/clojure/lang/Cons.java
@@ -16,6 +16,8 @@
 
 final public class Cons extends ASeq implements Serializable {
 
+private static final long serialVersionUID = 6682587018567831263L;
+
 private final Object _first;
 private final ISeq _more;
 
diff --git a/src/jvm/clojure/lang/Cycle.java b/src/jvm/clojure/lang/Cycle.java
index 877695558a..293463090c 100644
--- a/src/jvm/clojure/lang/Cycle.java
+++ b/src/jvm/clojure/lang/Cycle.java
@@ -14,6 +14,8 @@
 
 public class Cycle extends ASeq implements IReduce, IPending {
 
+private static final long serialVersionUID = 4007270937279943908L;
+
 private final ISeq all;      // never null
 private final ISeq prev;
 private volatile ISeq _current; // lazily realized
@@ -93,4 +95,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..f0aa855d20 100644
--- a/src/jvm/clojure/lang/Delay.java
+++ b/src/jvm/clojure/lang/Delay.java
@@ -12,15 +12,20 @@
 
 package clojure.lang;
 
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
 public class Delay implements IDeref, IPending{
-volatile Object val;
-volatile Throwable exception;
-volatile IFn fn;
-
-public Delay(IFn fn){
-	this.fn = fn;
-	this.val = null;
-        this.exception = null;
+Object val;
+Throwable exception;
+IFn fn;
+volatile Lock lock;
+
+public Delay(IFn f){
+	fn = f;
+	val = null;
+	exception = null;
+	lock = new ReentrantLock();
 }
 
 static public Object force(Object x) {
@@ -29,32 +34,35 @@ static public Object force(Object x) {
 	       : x;
 }
 
-public Object deref() {
-	if(fn != null)
-		{
-	        synchronized(this)
-	        {
-	        //double check
-	        if(fn!=null)
-	            {
-	                try
-	                    {
-	                    val = fn.invoke();
-	                    }
-	                catch(Throwable t)
-	                    {
-	                    exception = t;
-	                    }
-	                fn = null;
-	            }
-	        }
+private void realize() {
+	Lock l = lock;
+	if(l != null) {
+		l.lock();
+		try {
+			if(fn!=null) {
+				try {
+					val = fn.invoke();
+				} catch (Throwable t) {
+					exception = t;
+				}
+				fn = null;
+				lock = null;
+			}
+		} finally {
+			l.unlock();
 		}
+	}
+}
+
+public Object deref() {
+	if(lock != null)
+		realize();
 	if(exception != null)
 		throw Util.sneakyThrow(exception);
 	return val;
 }
 
-synchronized public boolean isRealized(){
-	return fn == null;
+public boolean isRealized(){
+	return lock == null;
 }
 }
diff --git a/src/jvm/clojure/lang/EnumerationSeq.java b/src/jvm/clojure/lang/EnumerationSeq.java
index 3616fa787c..acd13ca1e8 100644
--- a/src/jvm/clojure/lang/EnumerationSeq.java
+++ b/src/jvm/clojure/lang/EnumerationSeq.java
@@ -17,6 +17,9 @@
 import java.util.Enumeration;
 
 public class EnumerationSeq extends ASeq{
+
+private static final long serialVersionUID = 5227192199685595994L;
+
 final Enumeration iter;
 final State state;
 
diff --git a/src/jvm/clojure/lang/ExceptionInfo.java b/src/jvm/clojure/lang/ExceptionInfo.java
index 92040d21e0..28f845b7c3 100644
--- a/src/jvm/clojure/lang/ExceptionInfo.java
+++ b/src/jvm/clojure/lang/ExceptionInfo.java
@@ -16,6 +16,9 @@
  * exception classes.
  */
 public class ExceptionInfo extends RuntimeException implements IExceptionInfo {
+
+    private static final long serialVersionUID = -1073473305916521986L;
+
     public final IPersistentMap data;
 
     public ExceptionInfo(String s, IPersistentMap data) {
@@ -25,11 +28,7 @@ public ExceptionInfo(String s, IPersistentMap data) {
     public ExceptionInfo(String s, IPersistentMap data, Throwable throwable) {
         // null cause is equivalent to not passing a cause
         super(s, throwable);
-        if (data != null) {
-            this.data = data;
-        }  else {
-            throw new IllegalArgumentException("Additional data must be non-nil.");
-        }
+        this.data = (data == null) ? PersistentArrayMap.EMPTY: data;
     }
 
     public IPersistentMap getData() {
diff --git a/src/jvm/clojure/lang/FnInvokers.java b/src/jvm/clojure/lang/FnInvokers.java
new file mode 100644
index 0000000000..34cbaa548b
--- /dev/null
+++ b/src/jvm/clojure/lang/FnInvokers.java
@@ -0,0 +1,782 @@
+/**
+ *   Copyright (c) Rich Hickey. All rights reserved.
+ *   The use and distribution terms for this software are covered by the
+ *   Eclipse Public License 1.0 (https://opensource.org/license/epl-1-0)
+ *   which can be found in the file epl-v10.html at the root of this distribution.
+ *   By using this software in any fashion, you are agreeing to be bound by
+ * 	 the terms of this license.
+ *   You must not remove this notice, or any other, from this software.
+ **/
+
+package clojure.lang;
+
+public class FnInvokers {
+
+    // Encode invoker param/return class to code for method name
+    static char encodeInvokerType(Class c) {
+        if(Long.TYPE.equals(c)) {
+            return 'L';
+        } else if(Double.TYPE.equals(c)) {
+            return 'D';
+        } else if(Integer.TYPE.equals(c)) {
+            return 'I';
+        } else if(Short.TYPE.equals(c)) {
+            return 'S';
+        } else if(Byte.TYPE.equals(c)) {
+            return 'B';
+        } else if(Float.TYPE.equals(c)) {
+            return 'F';
+        } else {
+            return 'O';
+        }
+    }
+
+    public static long invokeL(IFn f0) {
+        if(f0 instanceof IFn.L) {
+            return ((IFn.L)f0).invokePrim();
+        } else {
+            return RT.longCast(f0.invoke());
+        }
+    }
+
+    public static int invokeI(IFn f0) {
+        if(f0 instanceof IFn.L) {
+            return RT.intCast(((IFn.L)f0).invokePrim());
+        } else {
+            return RT.intCast(f0.invoke());
+        }
+    }
+
+    public static short invokeS(IFn f0) {
+        if(f0 instanceof IFn.L) {
+            return RT.shortCast(((IFn.L)f0).invokePrim());
+        } else {
+            return RT.shortCast(f0.invoke());
+        }
+    }
+
+    public static byte invokeB(IFn f0) {
+        if(f0 instanceof IFn.L) {
+            return RT.byteCast(((IFn.L)f0).invokePrim());
+        } else {
+            return RT.byteCast(f0.invoke());
+        }
+    }
+
+    public static double invokeD(IFn f0) {
+        if(f0 instanceof IFn.D) {
+            return ((IFn.D)f0).invokePrim();
+        } else {
+            return RT.doubleCast(f0.invoke());
+        }
+    }
+
+    public static float invokeF(IFn f0) {
+        if(f0 instanceof IFn.D) {
+            return RT.floatCast(((IFn.D)f0).invokePrim());
+        } else {
+            return RT.floatCast(f0.invoke());
+        }
+    }
+
+    public static Object invokeO(IFn f0) {
+        return f0.invoke();
+    }
+
+    public static long invokeLL(IFn f0, long a) {
+        if(f0 instanceof IFn.LL) {
+            return ((IFn.LL)f0).invokePrim(a);
+        } else {
+            return RT.longCast(f0.invoke(a));
+        }
+    }
+
+    public static long invokeDL(IFn f0, double a) {
+        if(f0 instanceof IFn.DL) {
+            return ((IFn.DL)f0).invokePrim(a);
+        } else {
+            return RT.longCast(f0.invoke(a));
+        }
+    }
+
+    public static long invokeOL(IFn f0, Object a) {
+        if(f0 instanceof IFn.OL) {
+            return ((IFn.OL)f0).invokePrim(a);
+        } else {
+            return RT.longCast(f0.invoke(a));
+        }
+    }
+
+    public static int invokeLI(IFn f0, long a) {
+        if(f0 instanceof IFn.LL) {
+            return RT.intCast(((IFn.LL)f0).invokePrim(a));
+        } else {
+            return RT.intCast(f0.invoke(a));
+        }
+    }
+
+    public static int invokeDI(IFn f0, double a) {
+        if(f0 instanceof IFn.DL) {
+            return RT.intCast(((IFn.DL)f0).invokePrim(a));
+        } else {
+            return RT.intCast(f0.invoke(a));
+        }
+    }
+
+    public static int invokeOI(IFn f0, Object a) {
+        if(f0 instanceof IFn.OL) {
+            return RT.intCast(((IFn.OL)f0).invokePrim(a));
+        } else {
+            return RT.intCast(f0.invoke(a));
+        }
+    }
+
+    public static short invokeLS(IFn f0, long a) {
+        if(f0 instanceof IFn.LL) {
+            return RT.shortCast(((IFn.LL)f0).invokePrim(a));
+        } else {
+            return RT.shortCast(f0.invoke(a));
+        }
+    }
+
+    public static short invokeDS(IFn f0, double a) {
+        if(f0 instanceof IFn.DL) {
+            return RT.shortCast(((IFn.DL)f0).invokePrim(a));
+        } else {
+            return RT.shortCast(f0.invoke(a));
+        }
+    }
+
+    public static short invokeOS(IFn f0, Object a) {
+        if(f0 instanceof IFn.OL) {
+            return RT.shortCast(((IFn.OL)f0).invokePrim(a));
+        } else {
+            return RT.shortCast(f0.invoke(a));
+        }
+    }
+
+    public static byte invokeLB(IFn f0, long a) {
+        if(f0 instanceof IFn.LL) {
+            return RT.byteCast(((IFn.LL)f0).invokePrim(a));
+        } else {
+            return RT.byteCast(f0.invoke(a));
+        }
+    }
+
+    public static byte invokeDB(IFn f0, double a) {
+        if(f0 instanceof IFn.DL) {
+            return RT.byteCast(((IFn.DL)f0).invokePrim(a));
+        } else {
+            return RT.byteCast(f0.invoke(a));
+        }
+    }
+
+    public static byte invokeOB(IFn f0, Object a) {
+        if(f0 instanceof IFn.OL) {
+            return RT.byteCast(((IFn.OL)f0).invokePrim(a));
+        } else {
+            return RT.byteCast(f0.invoke(a));
+        }
+    }
+
+    public static double invokeLD(IFn f0, long a) {
+        if(f0 instanceof IFn.LD) {
+            return ((IFn.LD)f0).invokePrim(a);
+        } else {
+            return RT.doubleCast(f0.invoke(a));
+        }
+    }
+
+    public static double invokeDD(IFn f0, double a) {
+        if(f0 instanceof IFn.DD) {
+            return ((IFn.DD)f0).invokePrim(a);
+        } else {
+            return RT.doubleCast(f0.invoke(a));
+        }
+    }
+
+    public static double invokeOD(IFn f0, Object a) {
+        if(f0 instanceof IFn.OD) {
+            return ((IFn.OD)f0).invokePrim(a);
+        } else {
+            return RT.doubleCast(f0.invoke(a));
+        }
+    }
+
+    public static float invokeLF(IFn f0, long a) {
+        if(f0 instanceof IFn.LD) {
+            return RT.floatCast(((IFn.LD)f0).invokePrim(a));
+        } else {
+            return RT.floatCast(f0.invoke(a));
+        }
+    }
+
+    public static float invokeDF(IFn f0, double a) {
+        if(f0 instanceof IFn.DD) {
+            return RT.floatCast(((IFn.DD)f0).invokePrim(a));
+        } else {
+            return RT.floatCast(f0.invoke(a));
+        }
+    }
+
+    public static float invokeOF(IFn f0, Object a) {
+        if(f0 instanceof IFn.OD) {
+            return RT.floatCast(((IFn.OD)f0).invokePrim(a));
+        } else {
+            return RT.floatCast(f0.invoke(a));
+        }
+    }
+
+    public static Object invokeLO(IFn f0, long a) {
+        if(f0 instanceof IFn.LO) {
+            return ((IFn.LO)f0).invokePrim(a);
+        } else {
+            return f0.invoke(a);
+        }
+    }
+
+    public static Object invokeDO(IFn f0, double a) {
+        if(f0 instanceof IFn.DO) {
+            return ((IFn.DO)f0).invokePrim(a);
+        } else {
+            return f0.invoke(a);
+        }
+    }
+
+    public static Object invokeOO(IFn f0, Object a) {
+        return f0.invoke(a);
+    }
+
+    public static long invokeLLL(IFn f0, long a, long b) {
+        if(f0 instanceof IFn.LLL) {
+            return ((IFn.LLL)f0).invokePrim(a, b);
+        } else {
+            return RT.longCast(f0.invoke(a, b));
+        }
+    }
+
+    public static long invokeLOL(IFn f0, long a, Object b) {
+        if(f0 instanceof IFn.LOL) {
+            return ((IFn.LOL)f0).invokePrim(a, b);
+        } else {
+            return RT.longCast(f0.invoke(a, b));
+        }
+    }
+
+    public static long invokeOLL(IFn f0, Object a, long b) {
+        if(f0 instanceof IFn.OLL) {
+            return ((IFn.OLL)f0).invokePrim(a, b);
+        } else {
+            return RT.longCast(f0.invoke(a, b));
+        }
+    }
+
+    public static long invokeDDL(IFn f0, double a, double b) {
+        if(f0 instanceof IFn.DDL) {
+            return ((IFn.DDL)f0).invokePrim(a, b);
+        } else {
+            return RT.longCast(f0.invoke(a, b));
+        }
+    }
+
+    public static long invokeLDL(IFn f0, long a, double b) {
+        if(f0 instanceof IFn.LDL) {
+            return ((IFn.LDL)f0).invokePrim(a, b);
+        } else {
+            return RT.longCast(f0.invoke(a, b));
+        }
+    }
+
+    public static long invokeDLL(IFn f0, double a, long b) {
+        if(f0 instanceof IFn.DLL) {
+            return ((IFn.DLL)f0).invokePrim(a, b);
+        } else {
+            return RT.longCast(f0.invoke(a, b));
+        }
+    }
+
+    public static long invokeOOL(IFn f0, Object a, Object b) {
+        if(f0 instanceof IFn.OOL) {
+            return ((IFn.OOL)f0).invokePrim(a, b);
+        } else {
+            return RT.longCast(f0.invoke(a, b));
+        }
+    }
+
+    public static long invokeODL(IFn f0, Object a, double b) {
+        if(f0 instanceof IFn.ODL) {
+            return ((IFn.ODL)f0).invokePrim(a, b);
+        } else {
+            return RT.longCast(f0.invoke(a, b));
+        }
+    }
+
+    public static long invokeDOL(IFn f0, double a, Object b) {
+        if(f0 instanceof IFn.DOL) {
+            return ((IFn.DOL)f0).invokePrim(a, b);
+        } else {
+            return RT.longCast(f0.invoke(a, b));
+        }
+    }
+
+    public static int invokeLLI(IFn f0, long a, long b) {
+        if(f0 instanceof IFn.LLL) {
+            return RT.intCast(((IFn.LLL)f0).invokePrim(a, b));
+        } else {
+            return RT.intCast(f0.invoke(a, b));
+        }
+    }
+
+    public static int invokeLOI(IFn f0, long a, Object b) {
+        if(f0 instanceof IFn.LOL) {
+            return RT.intCast(((IFn.LOL)f0).invokePrim(a, b));
+        } else {
+            return RT.intCast(f0.invoke(a, b));
+        }
+    }
+
+    public static int invokeOLI(IFn f0, Object a, long b) {
+        if(f0 instanceof IFn.OLL) {
+            return RT.intCast(((IFn.OLL)f0).invokePrim(a, b));
+        } else {
+            return RT.intCast(f0.invoke(a, b));
+        }
+    }
+
+    public static int invokeDDI(IFn f0, double a, double b) {
+        if(f0 instanceof IFn.DDL) {
+            return RT.intCast(((IFn.DDL)f0).invokePrim(a, b));
+        } else {
+            return RT.intCast(f0.invoke(a, b));
+        }
+    }
+
+    public static int invokeLDI(IFn f0, long a, double b) {
+        if(f0 instanceof IFn.LDL) {
+            return RT.intCast(((IFn.LDL)f0).invokePrim(a, b));
+        } else {
+            return RT.intCast(f0.invoke(a, b));
+        }
+    }
+
+    public static int invokeDLI(IFn f0, double a, long b) {
+        if(f0 instanceof IFn.DLL) {
+            return RT.intCast(((IFn.DLL)f0).invokePrim(a, b));
+        } else {
+            return RT.intCast(f0.invoke(a, b));
+        }
+    }
+
+    public static int invokeOOI(IFn f0, Object a, Object b) {
+        if(f0 instanceof IFn.OOL) {
+            return RT.intCast(((IFn.OOL)f0).invokePrim(a, b));
+        } else {
+            return RT.intCast(f0.invoke(a, b));
+        }
+    }
+
+    public static int invokeODI(IFn f0, Object a, double b) {
+        if(f0 instanceof IFn.ODL) {
+            return RT.intCast(((IFn.ODL)f0).invokePrim(a, b));
+        } else {
+            return RT.intCast(f0.invoke(a, b));
+        }
+    }
+
+    public static int invokeDOI(IFn f0, double a, Object b) {
+        if(f0 instanceof IFn.DOL) {
+            return RT.intCast(((IFn.DOL)f0).invokePrim(a, b));
+        } else {
+            return RT.intCast(f0.invoke(a, b));
+        }
+    }
+
+    public static short invokeLLS(IFn f0, long a, long b) {
+        if(f0 instanceof IFn.LLL) {
+            return RT.shortCast(((IFn.LLL)f0).invokePrim(a, b));
+        } else {
+            return RT.shortCast(f0.invoke(a, b));
+        }
+    }
+
+    public static short invokeLOS(IFn f0, long a, Object b) {
+        if(f0 instanceof IFn.LOL) {
+            return RT.shortCast(((IFn.LOL)f0).invokePrim(a, b));
+        } else {
+            return RT.shortCast(f0.invoke(a, b));
+        }
+    }
+
+    public static short invokeOLS(IFn f0, Object a, long b) {
+        if(f0 instanceof IFn.OLL) {
+            return RT.shortCast(((IFn.OLL)f0).invokePrim(a, b));
+        } else {
+            return RT.shortCast(f0.invoke(a, b));
+        }
+    }
+
+    public static short invokeDDS(IFn f0, double a, double b) {
+        if(f0 instanceof IFn.DDL) {
+            return RT.shortCast(((IFn.DDL)f0).invokePrim(a, b));
+        } else {
+            return RT.shortCast(f0.invoke(a, b));
+        }
+    }
+
+    public static short invokeLDS(IFn f0, long a, double b) {
+        if(f0 instanceof IFn.LDL) {
+            return RT.shortCast(((IFn.LDL)f0).invokePrim(a, b));
+        } else {
+            return RT.shortCast(f0.invoke(a, b));
+        }
+    }
+
+    public static short invokeDLS(IFn f0, double a, long b) {
+        if(f0 instanceof IFn.DLL) {
+            return RT.shortCast(((IFn.DLL)f0).invokePrim(a, b));
+        } else {
+            return RT.shortCast(f0.invoke(a, b));
+        }
+    }
+
+    public static short invokeOOS(IFn f0, Object a, Object b) {
+        if(f0 instanceof IFn.OOL) {
+            return RT.shortCast(((IFn.OOL)f0).invokePrim(a, b));
+        } else {
+            return RT.shortCast(f0.invoke(a, b));
+        }
+    }
+
+    public static short invokeODS(IFn f0, Object a, double b) {
+        if(f0 instanceof IFn.ODL) {
+            return RT.shortCast(((IFn.ODL)f0).invokePrim(a, b));
+        } else {
+            return RT.shortCast(f0.invoke(a, b));
+        }
+    }
+
+    public static short invokeDOS(IFn f0, double a, Object b) {
+        if(f0 instanceof IFn.DOL) {
+            return RT.shortCast(((IFn.DOL)f0).invokePrim(a, b));
+        } else {
+            return RT.shortCast(f0.invoke(a, b));
+        }
+    }
+
+    public static byte invokeLLB(IFn f0, long a, long b) {
+        if(f0 instanceof IFn.LLL) {
+            return RT.byteCast(((IFn.LLL)f0).invokePrim(a, b));
+        } else {
+            return RT.byteCast(f0.invoke(a, b));
+        }
+    }
+
+    public static byte invokeLOB(IFn f0, long a, Object b) {
+        if(f0 instanceof IFn.LOL) {
+            return RT.byteCast(((IFn.LOL)f0).invokePrim(a, b));
+        } else {
+            return RT.byteCast(f0.invoke(a, b));
+        }
+    }
+
+    public static byte invokeOLB(IFn f0, Object a, long b) {
+        if(f0 instanceof IFn.OLL) {
+            return RT.byteCast(((IFn.OLL)f0).invokePrim(a, b));
+        } else {
+            return RT.byteCast(f0.invoke(a, b));
+        }
+    }
+
+    public static byte invokeDDB(IFn f0, double a, double b) {
+        if(f0 instanceof IFn.DDL) {
+            return RT.byteCast(((IFn.DDL)f0).invokePrim(a, b));
+        } else {
+            return RT.byteCast(f0.invoke(a, b));
+        }
+    }
+
+    public static byte invokeLDB(IFn f0, long a, double b) {
+        if(f0 instanceof IFn.LDL) {
+            return RT.byteCast(((IFn.LDL)f0).invokePrim(a, b));
+        } else {
+            return RT.byteCast(f0.invoke(a, b));
+        }
+    }
+
+    public static byte invokeDLB(IFn f0, double a, long b) {
+        if(f0 instanceof IFn.DLL) {
+            return RT.byteCast(((IFn.DLL)f0).invokePrim(a, b));
+        } else {
+            return RT.byteCast(f0.invoke(a, b));
+        }
+    }
+
+    public static byte invokeOOB(IFn f0, Object a, Object b) {
+        if(f0 instanceof IFn.OOL) {
+            return RT.byteCast(((IFn.OOL)f0).invokePrim(a, b));
+        } else {
+            return RT.byteCast(f0.invoke(a, b));
+        }
+    }
+
+    public static byte invokeODB(IFn f0, Object a, double b) {
+        if(f0 instanceof IFn.ODL) {
+            return RT.byteCast(((IFn.ODL)f0).invokePrim(a, b));
+        } else {
+            return RT.byteCast(f0.invoke(a, b));
+        }
+    }
+
+    public static byte invokeDOB(IFn f0, double a, Object b) {
+        if(f0 instanceof IFn.DOL) {
+            return RT.byteCast(((IFn.DOL)f0).invokePrim(a, b));
+        } else {
+            return RT.byteCast(f0.invoke(a, b));
+        }
+    }
+
+    public static double invokeLLD(IFn f0, long a, long b) {
+        if(f0 instanceof IFn.LLD) {
+            return ((IFn.LLD)f0).invokePrim(a, b);
+        } else {
+            return RT.doubleCast(f0.invoke(a, b));
+        }
+    }
+
+    public static double invokeLOD(IFn f0, long a, Object b) {
+        if(f0 instanceof IFn.LOD) {
+            return ((IFn.LOD)f0).invokePrim(a, b);
+        } else {
+            return RT.doubleCast(f0.invoke(a, b));
+        }
+    }
+
+    public static double invokeOLD(IFn f0, Object a, long b) {
+        if(f0 instanceof IFn.OLD) {
+            return ((IFn.OLD)f0).invokePrim(a, b);
+        } else {
+            return RT.doubleCast(f0.invoke(a, b));
+        }
+    }
+
+    public static double invokeDDD(IFn f0, double a, double b) {
+        if(f0 instanceof IFn.DDD) {
+            return ((IFn.DDD)f0).invokePrim(a, b);
+        } else {
+            return RT.doubleCast(f0.invoke(a, b));
+        }
+    }
+
+    public static double invokeLDD(IFn f0, long a, double b) {
+        if(f0 instanceof IFn.LDD) {
+            return ((IFn.LDD)f0).invokePrim(a, b);
+        } else {
+            return RT.doubleCast(f0.invoke(a, b));
+        }
+    }
+
+    public static double invokeDLD(IFn f0, double a, long b) {
+        if(f0 instanceof IFn.DLD) {
+            return ((IFn.DLD)f0).invokePrim(a, b);
+        } else {
+            return RT.doubleCast(f0.invoke(a, b));
+        }
+    }
+
+    public static double invokeOOD(IFn f0, Object a, Object b) {
+        if(f0 instanceof IFn.OOD) {
+            return ((IFn.OOD)f0).invokePrim(a, b);
+        } else {
+            return RT.doubleCast(f0.invoke(a, b));
+        }
+    }
+
+    public static double invokeODD(IFn f0, Object a, double b) {
+        if(f0 instanceof IFn.ODD) {
+            return ((IFn.ODD)f0).invokePrim(a, b);
+        } else {
+            return RT.doubleCast(f0.invoke(a, b));
+        }
+    }
+
+    public static double invokeDOD(IFn f0, double a, Object b) {
+        if(f0 instanceof IFn.DOD) {
+            return ((IFn.DOD)f0).invokePrim(a, b);
+        } else {
+            return RT.doubleCast(f0.invoke(a, b));
+        }
+    }
+
+    public static float invokeLLF(IFn f0, long a, long b) {
+        if(f0 instanceof IFn.LLD) {
+            return RT.floatCast(((IFn.LLD)f0).invokePrim(a, b));
+        } else {
+            return RT.floatCast(f0.invoke(a, b));
+        }
+    }
+
+    public static float invokeLOF(IFn f0, long a, Object b) {
+        if(f0 instanceof IFn.LOD) {
+            return RT.floatCast(((IFn.LOD)f0).invokePrim(a, b));
+        } else {
+            return RT.floatCast(f0.invoke(a, b));
+        }
+    }
+
+    public static float invokeOLF(IFn f0, Object a, long b) {
+        if(f0 instanceof IFn.OLD) {
+            return RT.floatCast(((IFn.OLD)f0).invokePrim(a, b));
+        } else {
+            return RT.floatCast(f0.invoke(a, b));
+        }
+    }
+
+    public static float invokeDDF(IFn f0, double a, double b) {
+        if(f0 instanceof IFn.DDD) {
+            return RT.floatCast(((IFn.DDD)f0).invokePrim(a, b));
+        } else {
+            return RT.floatCast(f0.invoke(a, b));
+        }
+    }
+
+    public static float invokeLDF(IFn f0, long a, double b) {
+        if(f0 instanceof IFn.LDD) {
+            return RT.floatCast(((IFn.LDD)f0).invokePrim(a, b));
+        } else {
+            return RT.floatCast(f0.invoke(a, b));
+        }
+    }
+
+    public static float invokeDLF(IFn f0, double a, long b) {
+        if(f0 instanceof IFn.DLD) {
+            return RT.floatCast(((IFn.DLD)f0).invokePrim(a, b));
+        } else {
+            return RT.floatCast(f0.invoke(a, b));
+        }
+    }
+
+    public static float invokeOOF(IFn f0, Object a, Object b) {
+        if(f0 instanceof IFn.OOD) {
+            return RT.floatCast(((IFn.OOD)f0).invokePrim(a, b));
+        } else {
+            return RT.floatCast(f0.invoke(a, b));
+        }
+    }
+
+    public static float invokeODF(IFn f0, Object a, double b) {
+        if(f0 instanceof IFn.ODD) {
+            return RT.floatCast(((IFn.ODD)f0).invokePrim(a, b));
+        } else {
+            return RT.floatCast(f0.invoke(a, b));
+        }
+    }
+
+    public static float invokeDOF(IFn f0, double a, Object b) {
+        if(f0 instanceof IFn.DOD) {
+            return RT.floatCast(((IFn.DOD)f0).invokePrim(a, b));
+        } else {
+            return RT.floatCast(f0.invoke(a, b));
+        }
+    }
+
+    public static Object invokeLLO(IFn f0, long a, long b) {
+        if(f0 instanceof IFn.LLO) {
+            return ((IFn.LLO)f0).invokePrim(a, b);
+        } else {
+            return f0.invoke(a, b);
+        }
+    }
+
+    public static Object invokeLOO(IFn f0, long a, Object b) {
+        if(f0 instanceof IFn.LOO) {
+            return ((IFn.LOO)f0).invokePrim(a, b);
+        } else {
+            return f0.invoke(a, b);
+        }
+    }
+
+    public static Object invokeOLO(IFn f0, Object a, long b) {
+        if(f0 instanceof IFn.OLO) {
+            return ((IFn.OLO)f0).invokePrim(a, b);
+        } else {
+            return f0.invoke(a, b);
+        }
+    }
+
+    public static Object invokeDDO(IFn f0, double a, double b) {
+        if(f0 instanceof IFn.DDO) {
+            return ((IFn.DDO)f0).invokePrim(a, b);
+        } else {
+            return f0.invoke(a, b);
+        }
+    }
+
+    public static Object invokeLDO(IFn f0, long a, double b) {
+        if(f0 instanceof IFn.LDO) {
+            return ((IFn.LDO)f0).invokePrim(a, b);
+        } else {
+            return f0.invoke(a, b);
+        }
+    }
+
+    public static Object invokeDLO(IFn f0, double a, long b) {
+        if(f0 instanceof IFn.DLO) {
+            return ((IFn.DLO)f0).invokePrim(a, b);
+        } else {
+            return f0.invoke(a, b);
+        }
+    }
+
+    public static Object invokeOOO(IFn f0, Object a, Object b) {
+        return f0.invoke(a, b);
+    }
+
+    public static Object invokeODO(IFn f0, Object a, double b) {
+        if(f0 instanceof IFn.ODO) {
+            return ((IFn.ODO)f0).invokePrim(a, b);
+        } else {
+            return f0.invoke(a, b);
+        }
+    }
+
+    public static Object invokeDOO(IFn f0, double a, Object b) {
+        if(f0 instanceof IFn.DOO) {
+            return ((IFn.DOO)f0).invokePrim(a, b);
+        } else {
+            return f0.invoke(a, b);
+        }
+    }
+
+    public static Object invokeOOOO(IFn f0, Object a, Object b, Object c) {
+        return f0.invoke(a, b, c);
+    }
+
+    public static Object invokeOOOOO(IFn f0, Object a, Object b, Object c, Object d) {
+        return f0.invoke(a, b, c, d);
+    }
+
+    public static Object invokeOOOOOO(IFn f0, Object a, Object b, Object c, Object d, Object e) {
+        return f0.invoke(a, b, c, d, e);
+    }
+
+    public static Object invokeOOOOOOO(IFn f0, Object a, Object b, Object c, Object d, Object e, Object f) {
+        return f0.invoke(a, b, c, d, e, f);
+    }
+
+    public static Object invokeOOOOOOOO(IFn f0, Object a, Object b, Object c, Object d, Object e, Object f, Object g) {
+        return f0.invoke(a, b, c, d, e, f, g);
+    }
+
+    public static Object invokeOOOOOOOOO(IFn f0, Object a, Object b, Object c, Object d, Object e, Object f, Object g, Object h) {
+        return f0.invoke(a, b, c, d, e, f, g, h);
+    }
+
+    public static Object invokeOOOOOOOOOO(IFn f0, Object a, Object b, Object c, Object d, Object e, Object f, Object g, Object h, Object i) {
+        return f0.invoke(a, b, c, d, e, f, g, h, i);
+    }
+
+    public static Object invokeOOOOOOOOOOO(IFn f0, Object a, Object b, Object c, Object d, Object e, Object f, Object g, Object h, Object i, Object j) {
+        return f0.invoke(a, b, c, d, e, f, g, h, i, j);
+    }
+
+}
\ No newline at end of file
diff --git a/src/jvm/clojure/lang/FnLoaderThunk.java b/src/jvm/clojure/lang/FnLoaderThunk.java
index 337ba2558c..1411bf4902 100644
--- a/src/jvm/clojure/lang/FnLoaderThunk.java
+++ b/src/jvm/clojure/lang/FnLoaderThunk.java
@@ -14,6 +14,8 @@
 
 public class FnLoaderThunk extends RestFn{
 
+private static final long serialVersionUID = 2194257205455463687L;
+
 final Var v;
 final ClassLoader loader;
 final String fnClassName;
@@ -51,7 +53,7 @@ private void load() {
 		{
 		try
 			{
-			fn = (IFn) Class.forName(fnClassName,true,loader).newInstance();
+			fn = (IFn) Class.forName(fnClassName,true,loader).getDeclaredConstructor().newInstance();
 			}
 		catch(Exception e)
 			{
diff --git a/src/jvm/clojure/lang/IDeref.java b/src/jvm/clojure/lang/IDeref.java
index 3a4746157b..0eeaaf329d 100644
--- a/src/jvm/clojure/lang/IDeref.java
+++ b/src/jvm/clojure/lang/IDeref.java
@@ -12,6 +12,38 @@
 
 package clojure.lang;
 
-public interface IDeref{
+import java.util.function.BooleanSupplier;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+
+public interface IDeref extends Supplier, BooleanSupplier, IntSupplier, LongSupplier, DoubleSupplier {
 Object deref() ;
+
+@Override
+default Object get() {
+    return deref();
+}
+
+@Override
+default boolean getAsBoolean() {
+    return RT.booleanCast(deref());
+}
+
+@Override
+default int getAsInt() {
+    return RT.intCast(deref());
+}
+
+@Override
+default long getAsLong() {
+    return RT.longCast(deref());
+}
+
+@Override
+default double getAsDouble() {
+    return RT.doubleCast(deref());
+}
+
 }
diff --git a/src/jvm/clojure/lang/IDrop.java b/src/jvm/clojure/lang/IDrop.java
new file mode 100644
index 0000000000..153ab0858f
--- /dev/null
+++ b/src/jvm/clojure/lang/IDrop.java
@@ -0,0 +1,27 @@
+/**
+ *   Copyright (c) Rich Hickey. All rights reserved.
+ *   The use and distribution terms for this software are covered by the
+ *   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+ *   which can be found in the file epl-v10.html at the root of this distribution.
+ *   By using this software in any fashion, you are agreeing to be bound by
+ * 	 the terms of this license.
+ *   You must not remove this notice, or any other, from this software.
+ **/
+
+package clojure.lang;
+
+/**
+ * Persistent or algorithmically defined collections can implement IDrop to provide
+ * a means of dropping N items that is more efficient than sequential walking.
+ */
+public interface IDrop{
+    /**
+     * Returns a collection that is Sequential, ISeq, and IReduceInit. It is also
+     * useful if the returned coll implements IDrop for subsequent use in a
+     * partition-like scenario.
+     *
+     * @param n Items to drop, must be > 0
+     * @return Collection that is Sequential, ISeq, and IReduceInit, or null if past the end
+     */
+    Sequential drop(int n);
+}
diff --git a/src/jvm/clojure/lang/Iterate.java b/src/jvm/clojure/lang/Iterate.java
index aec0c14aa2..a0562bce49 100644
--- a/src/jvm/clojure/lang/Iterate.java
+++ b/src/jvm/clojure/lang/Iterate.java
@@ -12,8 +12,12 @@
 
 /* Alex Miller, Dec 5, 2014 */
 
+import java.io.IOException;
+
 public class Iterate extends ASeq implements IReduce, IPending {
 
+private static final long serialVersionUID = -78221705247226450L;
+
 private static final Object UNREALIZED_SEED = new Object();
 private final IFn f;      // never null
 private final Object prevSeed;
@@ -84,4 +88,23 @@ public Object reduce(IFn rf, Object start){
         v = f.invoke(v);
     }
 }
+
+public int hashCode(){
+    throw new UnsupportedOperationException();
+}
+
+public int hasheq(){
+    throw new UnsupportedOperationException();
+}
+
+// serialization not supported
+private void writeObject(java.io.ObjectOutputStream out) throws IOException {
+    throw new UnsupportedOperationException();
+}
+
+// deserialization not supported
+private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+    throw new UnsupportedOperationException();
+}
+
 }
diff --git a/src/jvm/clojure/lang/IteratorSeq.java b/src/jvm/clojure/lang/IteratorSeq.java
index 9ecdbc48df..18b9cf3503 100644
--- a/src/jvm/clojure/lang/IteratorSeq.java
+++ b/src/jvm/clojure/lang/IteratorSeq.java
@@ -15,6 +15,9 @@
 import java.util.Iterator;
 
 public class IteratorSeq extends ASeq{
+
+private static final long serialVersionUID = -2631916503522522760L;
+
 final Iterator iter;
 final State state;
 
diff --git a/src/jvm/clojure/lang/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/LazySeq.java b/src/jvm/clojure/lang/LazySeq.java
index 0ca4e46223..cb8c617bfa 100644
--- a/src/jvm/clojure/lang/LazySeq.java
+++ b/src/jvm/clojure/lang/LazySeq.java
@@ -12,22 +12,30 @@
 
 package clojure.lang;
 
+import java.io.IOException;
+import java.io.ObjectInputStream;
 import java.util.*;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 public final class LazySeq extends Obj implements ISeq, Sequential, List, IPending, IHashEq{
 
-private IFn fn;
+private static final long serialVersionUID = -7531333024710395876L;
+
+private transient IFn fn;
 private Object sv;
 private ISeq s;
+private volatile Lock lock;
 
-public LazySeq(IFn fn){
-	this.fn = fn;
+public LazySeq(IFn f){
+	fn = f;
+	lock = new ReentrantLock();
 }
 
-private LazySeq(IPersistentMap meta, ISeq s){
+private LazySeq(IPersistentMap meta, ISeq seq){
 	super(meta);
-	this.fn = null;
-	this.s = s;
+	fn = null;
+	s = seq;
 }
 
 public Obj withMeta(IPersistentMap meta){
@@ -36,29 +44,64 @@ public Obj withMeta(IPersistentMap meta){
 	return new LazySeq(meta, seq());
 }
 
-final synchronized Object sval(){
-	if(fn != null)
-		{
-                sv = fn.invoke();
-                fn = null;
+// MUST be locked when called!
+final private void force() {
+	if (fn != null) {
+		sv = fn.invoke();
+		fn = null;
+	}
+}
+
+final private Object sval() {
+    Lock l = lock;
+    if(l != null) {
+        l.lock();
+        try {
+            //must re-examine under lock
+            if(lock != null) { //unrealized
+                force();
+                return sv;
+            }
+        } finally {
+            l.unlock();
+        }
+    }
+    // realized, read of lock above guarantees visibility of s
+    return s;
+}
+
+final private Object unwrap(Object ls){
+    while(ls instanceof LazySeq) {
+        ls = ((LazySeq) ls).sval();
+        }
+    return ls;
+}
+
+final private void realize() {
+	Lock l = lock;
+	if(l != null) {
+		l.lock();
+		try {
+            //must re-examine under lock
+            if(lock != null) {
+                force();
+                Object ls = sv;
+                sv = null;
+                if(ls instanceof LazySeq)
+                    ls = unwrap(ls);
+                s = RT.seq(ls);
+                lock = null;
+                }
+		    }
+        finally {
+			l.unlock();
 		}
-	if(sv != null)
-		return sv;
-	return s;
+	}
 }
 
-final synchronized public ISeq seq(){
-	sval();
-	if(sv != null)
-		{
-		Object ls = sv;
-		sv = null;
-		while(ls instanceof LazySeq)
-			{
-			ls = ((LazySeq)ls).sval();
-			}
-		s = RT.seq(ls);
-		}
+public final ISeq seq(){
+    if(lock != null)
+        realize();
 	return s;
 }
 
@@ -241,8 +284,18 @@ public boolean addAll(int index, Collection c){
 	throw new UnsupportedOperationException();
 }
 
+public boolean isRealized(){
+    return lock == null;
+}
 
-synchronized public boolean isRealized(){
-	return fn == null;
+// custom Serializable implementation - ensure seq is fully-realized before writing
+private void writeObject(java.io.ObjectOutputStream out) throws IOException {
+	ISeq s = this;
+	while(s != null) {
+		s = s.next();
+	}
+	out.defaultWriteObject();
 }
+
 }
+
diff --git a/src/jvm/clojure/lang/LispReader.java b/src/jvm/clojure/lang/LispReader.java
index 9fcb7b5aa9..8d7079cf5a 100644
--- a/src/jvm/clojure/lang/LispReader.java
+++ b/src/jvm/clojure/lang/LispReader.java
@@ -64,6 +64,7 @@ public class LispReader{
 static IFn[] dispatchMacros = new IFn[256];
 //static Pattern symbolPat = Pattern.compile("[:]?([\\D&&[^:/]][^:/]*/)?[\\D&&[^:/]][^:/]*");
 static Pattern symbolPat = Pattern.compile("[:]?([\\D&&[^/]].*/)?(/|[\\D&&[^/]][^/]*)");
+static Pattern arraySymbolPat = Pattern.compile("([\\D&&[^/:]].*)/([1-9])");
 //static Pattern varPat = Pattern.compile("([\\D&&[^:\\.]][^:\\.]*):([\\D&&[^:\\.]][^:\\.]*)");
 //static Pattern intPat = Pattern.compile("[-+]?[0-9]+\\.?");
 static Pattern intPat =
@@ -71,6 +72,7 @@ public class LispReader{
 				"([-+]?)(?:(0)|([1-9][0-9]*)|0[xX]([0-9A-Fa-f]+)|0([0-7]+)|([1-9][0-9]?)[rR]([0-9A-Za-z]+)|0[0-9]+)(N)?");
 static Pattern ratioPat = Pattern.compile("([-+]?[0-9]+)/([0-9]+)");
 static Pattern floatPat = Pattern.compile("([-+]?[0-9]+(\\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?");
+static Pattern argPat = Pattern.compile("%(?:(&)|([1-9][0-9]*))?");
 //static Pattern accessorPat = Pattern.compile("\\.[a-zA-Z_]\\w*");
 //static Pattern instanceMemberPat = Pattern.compile("\\.([a-zA-Z_][\\w\\.]*)\\.([a-zA-Z_]\\w*)");
 //static Pattern staticMemberPat = Pattern.compile("([a-zA-Z_][\\w\\.]*)\\.([a-zA-Z_]\\w*)");
@@ -460,6 +462,12 @@ private static Object matchSymbol(String s, Resolver resolver){
 			return Keyword.intern(sym);
 		return sym;
 		}
+	else
+		{
+		Matcher am = arraySymbolPat.matcher(s);
+		if(am.matches())
+			return Symbol.intern(am.group(1), am.group(2));
+		}
 	return null;
 }
 
@@ -920,23 +928,16 @@ static Symbol registerArg(int n){
 static class ArgReader extends AFn{
 	public Object invoke(Object reader, Object pct, Object opts, Object pendingForms) {
 		PushbackReader r = (PushbackReader) reader;
+		String token = readToken(r, '%');
 		if(ARG_ENV.deref() == null)
-			{
-			return interpretToken(readToken(r, '%'), null);
-			}
-		int ch = read1(r);
-		unread(r, ch);
-		//% alone is first arg
-		if(ch == -1 || isWhitespace(ch) || isTerminatingMacro(ch))
-			{
-			return registerArg(1);
-			}
-		Object n = read(r, true, null, true, opts, ensurePending(pendingForms));
-		if(n.equals(Compiler._AMP_))
-			return registerArg(-1);
-		if(!(n instanceof Number))
+			return interpretToken(token, null);
+
+		Matcher m = argPat.matcher(token);
+		if (!m.matches())
 			throw new IllegalStateException("arg literal must be %, %& or %integer");
-		return registerArg(((Number) n).intValue());
+		if (m.group(1) != null) // %&
+			return registerArg(-1);
+		return registerArg(m.group(2) == null ? 1 : Integer.parseInt(m.group(2)));
 	}
 }
 
@@ -956,8 +957,10 @@ public Object invoke(Object reader, Object caret, Object opts, Object pendingFor
 			meta = RT.map(RT.TAG_KEY, meta);
 		else if (meta instanceof Keyword)
 			meta = RT.map(meta, RT.T);
+		else if (meta instanceof IPersistentVector)
+			meta = RT.map(RT.PARAM_TAGS_KEY, meta);
 		else if(!(meta instanceof IPersistentMap))
-			throw new IllegalArgumentException("Metadata must be Symbol,Keyword,String or Map");
+			throw new IllegalArgumentException("Metadata must be Symbol,Keyword,String,Vector or Map");
 
 		Object o = read(r, true, null, true, opts, pendingForms);
 		if(o instanceof IMeta)
diff --git a/src/jvm/clojure/lang/LockingTransaction.java b/src/jvm/clojure/lang/LockingTransaction.java
index c28d2b2c9d..e76e90418d 100644
--- a/src/jvm/clojure/lang/LockingTransaction.java
+++ b/src/jvm/clojure/lang/LockingTransaction.java
@@ -102,7 +102,7 @@ void stop(int status){
 long readPoint;
 long startPoint;
 long startTime;
-final RetryEx retryex = new RetryEx();
+static final RetryEx retryex = new RetryEx();
 final ArrayList actions = new ArrayList();
 final HashMap vals = new HashMap();
 final HashSet sets = new HashSet();
diff --git a/src/jvm/clojure/lang/LongRange.java b/src/jvm/clojure/lang/LongRange.java
index 348e362081..10381cc29b 100644
--- a/src/jvm/clojure/lang/LongRange.java
+++ b/src/jvm/clojure/lang/LongRange.java
@@ -16,87 +16,82 @@
 import java.util.NoSuchElementException;
 
 /**
- * Implements the special common case of a finite range based on long start, end, and step.
+ * Implements the special common case of a finite range based on long start, end, and step,
+ * with no more than Integer.MAX_VALUE items.
  */
-public class LongRange extends ASeq implements Counted, IChunkedSeq, IReduce {
+public class LongRange extends ASeq implements Counted, IChunkedSeq, IReduce, IDrop {
 
-private static final int CHUNK_SIZE = 32;
+private static final long serialVersionUID = -1467242400566893909L;
 
 // Invariants guarantee this is never an empty or infinite seq
 //   assert(start != end && step != 0)
 final long start;
 final long end;
 final long step;
-final BoundsCheck boundsCheck;
-private volatile LongChunk _chunk;  // lazy
-private volatile ISeq _chunkNext;        // lazy
-private volatile ISeq _next;             // cached
-
-private static interface BoundsCheck extends Serializable {
-    boolean exceededBounds(long val);
-}
-
-private static BoundsCheck positiveStep(final long end) {
-    return new BoundsCheck() {
-        public boolean exceededBounds(long val){
-            return (val >= end);
-        }
-    };
-}
-
-private static BoundsCheck negativeStep(final long end) {
-    return new BoundsCheck() {
-        public boolean exceededBounds(long val){
-            return (val <= end);
-        }
-    };
-}
+final int count;
 
-private LongRange(long start, long end, long step, BoundsCheck boundsCheck){
+private LongRange(long start, long end, long step, int count){
     this.start = start;
     this.end = end;
     this.step = step;
-    this.boundsCheck = boundsCheck;
+    this.count = count;
 }
 
-private LongRange(long start, long end, long step, BoundsCheck boundsCheck, LongChunk chunk, ISeq chunkNext){
+private LongRange(IPersistentMap meta, long start, long end, long step, int count){
+    super(meta);
     this.start = start;
     this.end = end;
     this.step = step;
-    this.boundsCheck = boundsCheck;
-    this._chunk = chunk;
-    this._chunkNext = chunkNext;
+    this.count = count;
 }
 
-private LongRange(IPersistentMap meta, long start, long end, long step, BoundsCheck boundsCheck, LongChunk chunk, ISeq chunkNext){
-    super(meta);
-    this.start = start;
-    this.end = end;
-    this.step = step;
-    this.boundsCheck = boundsCheck;
-    this._chunk = chunk;
-    this._chunkNext = chunkNext;
+// returns exact size of remaining items OR throws ArithmeticException for overflow case
+static long rangeCount(long start, long end, long step) {
+    // (1) count = ceiling ( (end - start) / step )
+    // (2) ceiling(a/b) = (a+b+o)/b where o=-1 for positive stepping and +1 for negative stepping
+    // thus: count = end - start + step + o / step
+    return Numbers.add(Numbers.add(Numbers.minus(end, start), step), step > 0 ? -1 : 1) / step;
 }
 
 public static ISeq create(long end) {
-    if(end > 0)
-        return new LongRange(0L, end, 1L, positiveStep(end));
-    return PersistentList.EMPTY;
+    if(end > 0) {
+        try {
+            return new LongRange(0L, end, 1L, Math.toIntExact(rangeCount(0L, end, 1L)));
+        } catch(ArithmeticException e) {
+            return Range.create(end);  // count > Integer.MAX_VALUE
+        }
+    } else {
+        return PersistentList.EMPTY;
+    }
 }
 
 public static ISeq create(long start, long end) {
-    if(start >= end)
+    if(start >= end) {
         return PersistentList.EMPTY;
-    return new LongRange(start, end, 1L, positiveStep(end));
+    } else {
+        try {
+            return new LongRange(start, end, 1L, Math.toIntExact(rangeCount(start, end, 1L)));
+        } catch(ArithmeticException e) {
+            return Range.create(start, end);
+        }
+    }
 }
 
 public static ISeq create(final long start, long end, long step) {
     if(step > 0) {
         if(end <= start) return PersistentList.EMPTY;
-        return new LongRange(start, end, step, positiveStep(end));
+        try {
+            return new LongRange(start, end, step, Math.toIntExact(rangeCount(start, end, step)));
+        } catch(ArithmeticException e) {
+            return Range.create(start, end, step);
+        }
     } else if(step < 0) {
         if(end >= start) return PersistentList.EMPTY;
-        return new LongRange(start, end, step, negativeStep(end));
+        try {
+            return new LongRange(start, end, step, Math.toIntExact(rangeCount(start, end, step)));
+        } catch(ArithmeticException e) {
+            return Range.create(start, end, step);
+        }
     } else {
         if(end == start) return PersistentList.EMPTY;
         return Repeat.create(start);
@@ -106,51 +101,25 @@ public static ISeq create(final long start, long end, long step) {
 public Obj withMeta(IPersistentMap meta){
     if(meta == _meta)
         return this;
-    return new LongRange(meta, start, end, step, boundsCheck, _chunk, _chunkNext);
+    return new LongRange(meta, start, end, step, count);
 }
 
 public Object first() {
     return start;
 }
 
-public void forceChunk() {
-    if(_chunk != null) return;
-
-    long count;
-    try {
-        count = rangeCount(start, end, step);
-    } catch(ArithmeticException e) {
-        // size of total range is > Long.MAX_VALUE so must step to count
-        // this only happens in pathological range cases like:
-        // (range -9223372036854775808 9223372036854775807 9223372036854775807)
-        count = steppingCount(start, end, step);
-    }
-
-    if (count > CHUNK_SIZE) { // not last chunk
-        long nextStart = start + (step * CHUNK_SIZE);   // cannot overflow, must be < end
-        _chunkNext = new LongRange(nextStart, end, step, boundsCheck);
-        _chunk = new LongChunk(start, step, CHUNK_SIZE);
-    } else {  // last chunk
-        _chunk = new LongChunk(start, step, (int) count);   // count must be <= CHUNK_SIZE
-    }
-}
-
 public ISeq next() {
-    if(_next != null)
-        return _next;
-
-    forceChunk();
-    if(_chunk.count() > 1) {
-        LongChunk smallerChunk = _chunk.dropFirst();
-        _next = new LongRange(smallerChunk.first(), end, step, boundsCheck, smallerChunk, _chunkNext);
-        return _next;
+    if(count > 1) {
+        return new LongRange(start + step, end, step, count-1);
+    } else {
+        return null;
     }
-    return chunkedNext();
 }
 
+private static final int CHUNK_SIZE = 32;
+
 public IChunk chunkedFirst() {
-    forceChunk();
-    return _chunk;
+    return new LongChunk(start, step, Math.min(count, CHUNK_SIZE));
 }
 
 public ISeq chunkedNext() {
@@ -158,82 +127,51 @@ public ISeq chunkedNext() {
 }
 
 public ISeq chunkedMore() {
-    forceChunk();
-    if(_chunkNext == null)
+    if(count <= CHUNK_SIZE) {
         return PersistentList.EMPTY;
-    return _chunkNext;
-}
-
-// fallback count mechanism for pathological cases
-// returns either exact count or CHUNK_SIZE+1
-long steppingCount(long start, long end, long step) {
-    long count = 1;
-    long s = start;
-    while(count <= CHUNK_SIZE) {
-        try {
-            s = Numbers.add(s, step);
-            if(boundsCheck.exceededBounds(s))
-                break;
-            else
-                count++;
-        } catch(ArithmeticException e) {
-            break;
-        }
+    } else {
+        return LongRange.create(start + (step * CHUNK_SIZE), end, step);
     }
-    return count;
+
 }
 
-// returns exact size of remaining items OR throws ArithmeticException for overflow case
-long rangeCount(long start, long end, long step) {
-    // (1) count = ceiling ( (end - start) / step )
-    // (2) ceiling(a/b) = (a+b+o)/b where o=-1 for positive stepping and +1 for negative stepping
-    // thus: count = end - start + step + o / step
-    return Numbers.add(Numbers.add(Numbers.minus(end, start), step), this.step > 0 ? -1 : 1) / step;
+public Sequential drop(int n) {
+    if(n <= 0) {
+        return this;
+    } else if(n < count) {
+        return new LongRange(start+(step*n), end, step, count - n);
+    } else {
+        return null;
+    }
 }
 
 public int count() {
-    try {
-        long c = rangeCount(start, end, step);
-        if(c > Integer.MAX_VALUE) {
-            return Numbers.throwIntOverflow();
-        } else {
-            return (int) c;
-        }
-    } catch(ArithmeticException e) {
-        // rare case from large range or step, fall back to iterating and counting
-        Iterator iter = this.iterator();
-        long count = 0;
-        while(iter.hasNext()) {
-            iter.next();
-            count++;
-        }
-
-        if(count > Integer.MAX_VALUE)
-            return Numbers.throwIntOverflow();
-        else
-            return (int)count;
-    }
+    return count;
 }
 
 public Object reduce(IFn f) {
     Object acc = start;
     long i = start + step;
-    while(! boundsCheck.exceededBounds(i)) {
+    int n = count;
+    while(n > 1) {
         acc = f.invoke(acc, i);
         if (acc instanceof Reduced) return ((Reduced)acc).deref();
         i += step;
+        n--;
     }
     return acc;
 }
 
 public Object reduce(IFn f, Object val) {
     Object acc = val;
+    int n = count;
     long i = start;
     do {
         acc = f.invoke(acc, i);
         if (RT.isReduced(acc)) return ((Reduced)acc).deref();
         i += step;
-    } while(! boundsCheck.exceededBounds(i));
+        n--;
+    } while(n > 0);
     return acc;
 }
 
@@ -243,26 +181,22 @@ public Iterator iterator() {
 
 class LongRangeIterator implements Iterator {
     private long next;
-    private boolean hasNext;
+    private int remaining;
 
     public LongRangeIterator() {
         this.next = start;
-        this.hasNext = true;
+        this.remaining = count;
     }
 
     public boolean hasNext() {
-        return hasNext;
+        return remaining > 0;
     }
 
     public Object next() {
-        if (hasNext) {
+        if (remaining > 0) {
             long ret = next;
-            try {
-                next = Numbers.add(next, step);
-                hasNext = ! boundsCheck.exceededBounds(next);
-            } catch(ArithmeticException e) {
-                hasNext = false;
-            }
+            next = next + step;
+            remaining = remaining - 1;
             return ret;
         } else {
             throw new NoSuchElementException();
@@ -322,4 +256,4 @@ public Object reduce(IFn f, Object init) {
     }
 
 }
-}
\ No newline at end of file
+}
diff --git a/src/jvm/clojure/lang/MapEntry.java b/src/jvm/clojure/lang/MapEntry.java
index 310199c1db..777b262734 100644
--- a/src/jvm/clojure/lang/MapEntry.java
+++ b/src/jvm/clojure/lang/MapEntry.java
@@ -13,6 +13,9 @@
 import java.util.Iterator;
 
 public class MapEntry extends AMapEntry{
+
+private static final long serialVersionUID = -3752414622414469244L;
+
 final Object _key;
 final Object _val;
 
diff --git a/src/jvm/clojure/lang/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/Namespace.java b/src/jvm/clojure/lang/Namespace.java
index 68cded632e..359815778c 100644
--- a/src/jvm/clojure/lang/Namespace.java
+++ b/src/jvm/clojure/lang/Namespace.java
@@ -18,6 +18,9 @@
 import java.util.concurrent.atomic.AtomicReference;
 
 public class Namespace extends AReference implements Serializable {
+
+private static final long serialVersionUID = -3134444395801383865L;
+
 final public Symbol name;
 transient final AtomicReference mappings = new AtomicReference();
 transient final AtomicReference aliases = new AtomicReference();
@@ -47,11 +50,22 @@ public IPersistentMap getMappings(){
 	return mappings.get();
 }
 
+/**
+ * An interned mapping is one where a var's ns matches the current ns and its sym matches the mapping key.
+ * Once established, interned mappings should never change.
+ */
+private boolean isInternedMapping(Symbol sym, Object o){
+	return(o instanceof Var &&
+			((Var) o).ns == this &&
+			((Var) o).sym.equals(sym));
+}
+
 public Var intern(Symbol sym){
 	if(sym.ns != null)
 		{
 		throw new IllegalArgumentException("Can't intern namespace-qualified symbol");
 		}
+
 	IPersistentMap map = getMappings();
 	Object o;
 	Var v = null;
@@ -63,32 +77,50 @@ public Var intern(Symbol sym){
 		mappings.compareAndSet(map, newMap);
 		map = getMappings();
 		}
-	if(o instanceof Var && ((Var) o).ns == this)
+	if(isInternedMapping(sym, o))
 		return (Var) o;
 
 	if(v == null)
 		v = new Var(this, sym);
 
-	warnOrFailOnReplace(sym, o, v);
-
+	if(checkReplacement(sym, o, v)){
+		while (!mappings.compareAndSet(map, map.assoc(sym, v)))
+			map = getMappings();
 
-	while(!mappings.compareAndSet(map, map.assoc(sym, v)))
-		map = getMappings();
+		return v;
+	}
 
-	return v;
+	return (Var) o;
 }
 
-private void warnOrFailOnReplace(Symbol sym, Object o, Object v){
-    if (o instanceof Var)
-        {
-        Namespace ns = ((Var)o).ns;
-        if (ns == this || (v instanceof Var && ((Var)v).ns  == RT.CLOJURE_NS))
-            return;
-        if (ns != RT.CLOJURE_NS)
-            throw new IllegalStateException(sym + " already refers to: " + o + " in namespace: " + name);
-        }
-	RT.errPrintWriter().println("WARNING: " + sym + " already refers to: " + o + " in namespace: " + name
-		+ ", being replaced by: " + v);
+/*
+ This method checks if a namespace's mapping is applicable and warns on problematic cases.
+ It will return a boolean indicating if a mapping is replaceable.
+ The semantics of what constitutes a legal replacement mapping is summarized as follows:
+
+| classification | in namespace ns        | newval = anything other than ns/name | newval = ns/name                    |
+|----------------+------------------------+--------------------------------------+-------------------------------------|
+| native mapping | name -> ns/name        | no replace, warn-if newval not-core  | no replace, warn-if newval not-core |
+| alias mapping  | name -> other/whatever | warn + replace                       | warn + replace                      |
+*/
+private boolean checkReplacement(Symbol sym, Object old, Object neu){
+	if(old instanceof Var) {
+		Namespace ons = ((Var)old).ns;
+		Namespace nns = neu instanceof Var ? ((Var) neu).ns : null;
+
+		if(isInternedMapping(sym, old)){
+			if(nns != RT.CLOJURE_NS){
+				RT.errPrintWriter().println("REJECTED: attempt to replace interned var "
+						+ old +  " with " + neu + " in " + name + ", you must ns-unmap first");
+				return false;
+			}
+			else
+				return false;
+		}
+	}
+	RT.errPrintWriter().println("WARNING: " + sym + " already refers to: " + old + " in namespace: " + name
+			+ ", being replaced by: " + neu);
+	return true;
 }
 
 Object reference(Symbol sym, Object val){
@@ -96,6 +128,7 @@ Object reference(Symbol sym, Object val){
 		{
 		throw new IllegalArgumentException("Can't intern namespace-qualified symbol");
 		}
+
 	IPersistentMap map = getMappings();
 	Object o;
 	while((o = map.valAt(sym)) == null)
@@ -107,13 +140,14 @@ Object reference(Symbol sym, Object val){
 	if(o == val)
 		return o;
 
-	warnOrFailOnReplace(sym, o, val);
-
-	while(!mappings.compareAndSet(map, map.assoc(sym, val)))
-		map = getMappings();
+	if(checkReplacement(sym, o, val)){
+		while (!mappings.compareAndSet(map, map.assoc(sym, val)))
+			map = getMappings();
 
-	return val;
+		return val;
+	}
 
+	return o;
 }
 
 public static boolean areDifferentInstancesOfSameClassName(Class cls1, Class cls2) {
diff --git a/src/jvm/clojure/lang/Numbers.java b/src/jvm/clojure/lang/Numbers.java
index fa13cba62c..661c051d36 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{
@@ -528,6 +530,10 @@ static long gcd(long u, long v){
 	public Number divide(Number x, Number y){
 		long n = x.longValue();
 		long val = y.longValue();
+
+		if(n == Long.MIN_VALUE || val == Long.MIN_VALUE)
+			return BIGINT_OPS.divide(x, y);
+
 		long gcd = gcd(n, val);
 		if(gcd == 0)
 			return num(0);
@@ -619,6 +625,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 +716,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 +851,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 +954,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 +1077,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 +1919,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 +1930,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 +1941,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 +1951,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 +1961,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 +1972,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 +4082,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 +4176,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 +4227,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/Obj.java b/src/jvm/clojure/lang/Obj.java
index bd7ee91878..655d09117d 100644
--- a/src/jvm/clojure/lang/Obj.java
+++ b/src/jvm/clojure/lang/Obj.java
@@ -16,6 +16,8 @@
 
 public abstract class Obj implements IObj, Serializable {
 
+private static final long serialVersionUID = 802029099426284526L;
+
 final IPersistentMap _meta;
 
 public Obj(IPersistentMap meta){
diff --git a/src/jvm/clojure/lang/PersistentArrayMap.java b/src/jvm/clojure/lang/PersistentArrayMap.java
index 27b860e5d5..ccd542d638 100644
--- a/src/jvm/clojure/lang/PersistentArrayMap.java
+++ b/src/jvm/clojure/lang/PersistentArrayMap.java
@@ -27,10 +27,13 @@
  * 

null keys and values are ok, but you won't be able to distinguish a null value via valAt - use contains/entryAt

*/ -public class PersistentArrayMap extends APersistentMap implements IObj, IEditableCollection, IMapIterable, IKVReduce{ +public class PersistentArrayMap extends APersistentMap implements IObj, IEditableCollection, IMapIterable, IKVReduce, IDrop{ + +private static final long serialVersionUID = -2074065891090893601L; final Object[] array; static final int HASHTABLE_THRESHOLD = 16; +static final int KW_HASHTABLE_THRESHOLD = 128; public static final PersistentArrayMap EMPTY = new PersistentArrayMap(); private final IPersistentMap _meta; @@ -76,9 +79,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 @@ -179,7 +234,9 @@ public IPersistentMap assocEx(Object key, Object val) { } else //didn't have key, grow { - if(array.length >= HASHTABLE_THRESHOLD) + boolean isKW = key instanceof Keyword; + if((isKW && array.length >= KW_HASHTABLE_THRESHOLD) + || (!isKW && array.length >= HASHTABLE_THRESHOLD)) return createHT(array).assocEx(key, val); newArray = new Object[array.length + 2]; if(array.length > 0) @@ -202,7 +259,9 @@ public IPersistentMap assoc(Object key, Object val){ } else //didn't have key, grow { - if(array.length >= HASHTABLE_THRESHOLD) + boolean isKW = key instanceof Keyword; + if((isKW && array.length >= KW_HASHTABLE_THRESHOLD) + || (!isKW && array.length >= HASHTABLE_THRESHOLD)) return createHT(array).assoc(key, val); newArray = new Object[array.length + 2]; if(array.length > 0) @@ -296,11 +355,19 @@ public ISeq seq(){ return null; } +public Sequential drop(int n) { + if(array.length > 0) { + return ((Seq) seq()).drop(n); + } else { + return null; + } +} + public IPersistentMap meta(){ return _meta; } -static class Seq extends ASeq implements Counted{ +static class Seq extends ASeq implements Counted, IReduce, IDrop { final Object[] array; final int i; @@ -329,16 +396,52 @@ public int count(){ return (array.length - i) / 2; } + public Sequential drop(int n) { + if(n < count()) { + return new Seq(array, i + (2 * n)); + } else { + return null; + } + } + public Obj withMeta(IPersistentMap meta){ if(meta() == meta) return this; return new Seq(meta, array, i); } + + public Iterator iterator() { + return new Iter(array, i-2, APersistentMap.MAKE_ENTRY); + } + + public Object reduce(IFn f) { + if(i < array.length) { + Object acc = MapEntry.create(array[i], array[i+1]); + for(int j=i+2;j < array.length;j+=2){ + acc = f.invoke(acc, MapEntry.create(array[j], array[j+1])); + if(RT.isReduced(acc)) + return ((IDeref)acc).deref(); + } + return acc; + } else { + return f.invoke(); + } + } + + public Object reduce(IFn f, Object init) { + Object acc = init; + for(int j=i;j < array.length;j+=2){ + acc = f.invoke(acc, MapEntry.create(array[j], array[j+1])); + if(RT.isReduced(acc)) + return ((IDeref)acc).deref(); + } + return acc; + } } static class Iter implements Iterator{ - IFn f; - Object[] array; + final IFn f; + final Object[] array; int i; //for iterator diff --git a/src/jvm/clojure/lang/PersistentHashMap.java b/src/jvm/clojure/lang/PersistentHashMap.java index 0cb1259796..8f15944bc6 100644 --- a/src/jvm/clojure/lang/PersistentHashMap.java +++ b/src/jvm/clojure/lang/PersistentHashMap.java @@ -27,6 +27,8 @@ public class PersistentHashMap extends APersistentMap implements IEditableCollection, IObj, IMapIterable, IKVReduce { +private static final long serialVersionUID = -8682496769319143320L; + final int count; final INode root; final boolean hasNull; diff --git a/src/jvm/clojure/lang/PersistentHashSet.java b/src/jvm/clojure/lang/PersistentHashSet.java index d3a7162605..b481a9443c 100644 --- a/src/jvm/clojure/lang/PersistentHashSet.java +++ b/src/jvm/clojure/lang/PersistentHashSet.java @@ -16,6 +16,8 @@ public class PersistentHashSet extends APersistentSet implements IObj, IEditableCollection { +private static final long serialVersionUID = 6973890746204954938L; + static public final PersistentHashSet EMPTY = new PersistentHashSet(null, PersistentHashMap.EMPTY); final IPersistentMap _meta; diff --git a/src/jvm/clojure/lang/PersistentList.java b/src/jvm/clojure/lang/PersistentList.java index 618d52fca0..225651d663 100644 --- a/src/jvm/clojure/lang/PersistentList.java +++ b/src/jvm/clojure/lang/PersistentList.java @@ -15,6 +15,8 @@ public class PersistentList extends ASeq implements IPersistentList, IReduce, List, Counted { +private static final long serialVersionUID = -8833289659955219995L; + private final Object _first; private final IPersistentList _rest; private final int _count; diff --git a/src/jvm/clojure/lang/PersistentQueue.java b/src/jvm/clojure/lang/PersistentQueue.java index af916193e9..881c190028 100644 --- a/src/jvm/clojure/lang/PersistentQueue.java +++ b/src/jvm/clojure/lang/PersistentQueue.java @@ -24,6 +24,8 @@ public class PersistentQueue extends Obj implements IPersistentList, Collection, Counted, IHashEq{ +private static final long serialVersionUID = 8247184423915313132L; + final public static PersistentQueue EMPTY = new PersistentQueue(null, 0, null, null); //* diff --git a/src/jvm/clojure/lang/PersistentStructMap.java b/src/jvm/clojure/lang/PersistentStructMap.java index 734428c06c..7e9c92e317 100644 --- a/src/jvm/clojure/lang/PersistentStructMap.java +++ b/src/jvm/clojure/lang/PersistentStructMap.java @@ -19,6 +19,8 @@ public class PersistentStructMap extends APersistentMap implements IObj{ +private static final long serialVersionUID = -2701411408470234065L; + public static class Def implements Serializable{ final ISeq keys; final IPersistentMap keyslots; diff --git a/src/jvm/clojure/lang/PersistentVector.java b/src/jvm/clojure/lang/PersistentVector.java index fcd9e46c26..2ac8b1c142 100644 --- a/src/jvm/clojure/lang/PersistentVector.java +++ b/src/jvm/clojure/lang/PersistentVector.java @@ -17,9 +17,13 @@ import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; +import java.util.Spliterator; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; -public class PersistentVector extends APersistentVector implements IObj, IEditableCollection, IReduce, IKVReduce{ +public class PersistentVector extends APersistentVector implements IObj, IEditableCollection, IReduce, IKVReduce, IDrop{ + +private static final long serialVersionUID = -7896022351281214157L; public static class Node implements Serializable { transient public final AtomicReference edit; @@ -316,6 +320,77 @@ public void remove(){ public Iterator iterator(){return rangedIterator(0,count());} +@Override +Spliterator rangedSpliterator(final int start, final int end){ + return new Spliterator(){ + int i = start; + int base = i - (i%32); + Object[] array = (start < count())?arrayFor(i):null; + + @Override + public int characteristics() { + return Spliterator.IMMUTABLE | // persistent + Spliterator.ORDERED | // know order + Spliterator.SIZED | // know size + Spliterator.SUBSIZED; // know size after split + } + + @Override + public long estimateSize() { + return end-i; + } + + @Override + public long getExactSizeIfKnown() { + return end-i; + } + + @Override + public boolean tryAdvance(Consumer action) { + if(i < end) { + if(i-base == 32){ + array = arrayFor(i); + base += 32; + } + action.accept(array[i++ & 0x01f]); + return true; + } + return false; + } + + @Override + public Spliterator trySplit() { + int lo = i; + int mid = (lo + end) >>> 1; // avoid overflow + if(lo >= mid) { + return null; + } else { + i = mid; + return rangedSpliterator(lo, mid); + } + } + + @Override + public void forEachRemaining(Consumer action) { + int x=i; + while(x 0) @@ -363,7 +438,16 @@ public Object kvreduce(IFn f, Object init){ return init; } -static public final class ChunkedSeq extends ASeq implements IChunkedSeq,Counted{ +public Sequential drop(int n) { + if(n < cnt) { + int offset = n%32; + return new ChunkedSeq(this, this.arrayFor(n), n-offset, offset); + } else { + return null; + } +} + +static public final class ChunkedSeq extends ASeq implements IChunkedSeq,Counted,IReduce,IDrop{ public final PersistentVector vec; final Object[] node; @@ -428,6 +512,72 @@ public ISeq next(){ public int count(){ return vec.cnt - (i + offset); } + + public Iterator iterator() { + return vec.rangedIterator(i +offset, vec.cnt); + } + + public Object reduce(IFn f) { + Object acc; + if (i +offset < vec.cnt) + acc = node[offset]; + else + return f.invoke(); + + for(int j=offset+1;j 0 && n <= m.groupCount()) return m.group(n); return notFound; } @@ -1243,10 +1264,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/Range.java b/src/jvm/clojure/lang/Range.java index 4f638e9f97..1e4fbf6e01 100644 --- a/src/jvm/clojure/lang/Range.java +++ b/src/jvm/clojure/lang/Range.java @@ -18,6 +18,8 @@ */ public class Range extends ASeq implements IChunkedSeq, IReduce { +private static final long serialVersionUID = -71973733672395145L; + private static final int CHUNK_SIZE = 32; // Invariants guarantee this is never an "empty" seq @@ -99,7 +101,7 @@ public static ISeq create(final Object start, Object end, Object step) { public Obj withMeta(IPersistentMap meta){ if(meta == _meta) return this; - return new Range(meta, end, start, step, boundsCheck, _chunk, _chunkNext); + return new Range(meta, start, end, step, boundsCheck, _chunk, _chunkNext); } public Object first(){ diff --git a/src/jvm/clojure/lang/Ratio.java b/src/jvm/clojure/lang/Ratio.java index 6c7a9bb6e2..02af3fdea2 100644 --- a/src/jvm/clojure/lang/Ratio.java +++ b/src/jvm/clojure/lang/Ratio.java @@ -17,6 +17,9 @@ import java.math.MathContext; public class Ratio extends Number implements Comparable{ + +private static final long serialVersionUID = -576272795628662988L; + final public BigInteger numerator; final public BigInteger denominator; diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index d5811cc59d..14cef154b9 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -19,6 +19,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; import java.util.*; import java.util.stream.Collectors; @@ -94,12 +95,19 @@ private static Method toAccessibleSuperMethod(Method m, Object target) { } public static Object invokeInstanceMethod(Object target, String methodName, Object[] args) { - Class c = target.getClass(); + return invokeInstanceMethodOfClass(target, target.getClass(), methodName, args); +} + +public static Object invokeInstanceMethodOfClass(Object target, Class c, String methodName, Object[] args) { List methods = getMethods(c, args.length, methodName, false).stream() - .map(method -> toAccessibleSuperMethod(method, target)) - .filter(method -> (method != null)) - .collect(Collectors.toList()); - return invokeMatchingMethod(methodName, methods, target, args); + .map(method -> toAccessibleSuperMethod(method, target)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + return invokeMatchingMethod(methodName, methods, c, target, args); +} + +public static Object invokeInstanceMethodOfClass(Object target, String className, String methodName, Object[] args) { + return invokeInstanceMethodOfClass(target, RT.classForName(className), methodName, args); } private static Throwable getCauseOrElse(Exception e) { @@ -114,57 +122,78 @@ private static RuntimeException throwCauseOrElseException(Exception e) { throw Util.sneakyThrow(e); } -private static String noMethodReport(String methodName, Object target, Object[] args){ +private static String noMethodReport(String methodName, Class contextClass, Object[] args){ return "No matching method " + methodName + " found taking " + args.length + " args" - + (target==null?"":" for " + target.getClass()); + + (contextClass != null ? " for " + contextClass : ""); +} + +private static Method matchMethod(List methods, Object[] args) { + Method foundm = null; + for(Iterator i = methods.iterator(); i.hasNext();) { + Method m = (Method) i.next(); + Class[] params = m.getParameterTypes(); + if(isCongruent(params, args) && (foundm == null || Compiler.subsumes(params, foundm.getParameterTypes()))) + foundm = m; + } + return foundm; +} + +private static Object[] widenBoxedArgs(Object[] args) { + Object[] widenedArgs = new Object[args.length]; + for(int i=0; i getMethods(Class c, int arity, String name, boolean g return methods; } +// Return type coercions match coercions in FnInvokers for compiled invokers +private static Object coerceAdapterReturn(Object ret, Class targetType) { + if(targetType.isPrimitive()) { + switch (targetType.getName()) { + case "boolean": return RT.booleanCast(ret); + case "long": return RT.longCast(ret); + case "double": return RT.doubleCast(ret); + case "int": return RT.intCast(ret); + case "short": return RT.shortCast(ret); + case "byte": return RT.byteCast(ret); + case "float": return RT.floatCast(ret); + } + } + return ret; +} static Object boxArg(Class paramType, Object arg){ - if(!paramType.isPrimitive()) + if(arg instanceof IFn && Compiler.FISupport.maybeFIMethod(paramType) != null && !(paramType.isInstance(arg))) + // Adapt IFn obj to targetType using dynamic proxy + return Proxy.newProxyInstance(RT.baseLoader(), + new Class[]{paramType}, + (proxy, method, methodArgs) -> { + Object ret = ((IFn) arg).applyTo(RT.seq(methodArgs)); + return coerceAdapterReturn(ret, method.getReturnType()); + }); + else if(!paramType.isPrimitive()) return paramType.cast(arg); else if(paramType == boolean.class) return Boolean.class.cast(arg); @@ -592,6 +644,8 @@ static public boolean paramArgTypeMatch(Class paramType, Class argType){ return !paramType.isPrimitive(); if(paramType == argType || paramType.isAssignableFrom(argType)) return true; + if(Compiler.FISupport.maybeFIMethod(paramType) != null && IFn.class.isAssignableFrom(argType)) + return true; if(paramType == int.class) return argType == Integer.class || argType == long.class @@ -651,4 +705,5 @@ public static Object prepRet(Class c, Object x){ // return Double.valueOf(((Float) x).doubleValue()); return x; } + } diff --git a/src/jvm/clojure/lang/Repeat.java b/src/jvm/clojure/lang/Repeat.java index 2ce4f2dab2..7e11808385 100644 --- a/src/jvm/clojure/lang/Repeat.java +++ b/src/jvm/clojure/lang/Repeat.java @@ -12,7 +12,9 @@ /* Alex Miller, Dec 5, 2014 */ -public class Repeat extends ASeq implements IReduce { +public class Repeat extends ASeq implements IReduce, IDrop { + +private static final long serialVersionUID = -5140377547192202551L; private static final long INFINITE = -1; @@ -97,4 +99,31 @@ public Object reduce(IFn f, Object start){ } } +public Sequential drop(int n) { + if(count == INFINITE) { + return this; + } else { + long droppedCount = count-n; + if(droppedCount > 0) { + return new Repeat(droppedCount, val); + } else { + return null; + } + } +} + +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/src/jvm/clojure/lang/RestFn.java b/src/jvm/clojure/lang/RestFn.java index 98646835c3..f6d844302a 100644 --- a/src/jvm/clojure/lang/RestFn.java +++ b/src/jvm/clojure/lang/RestFn.java @@ -10,6 +10,9 @@ package clojure.lang; public abstract class RestFn extends AFunction{ + +private static final long serialVersionUID = -4319097849151802009L; + abstract public int getRequiredArity(); protected Object doInvoke(Object args) { diff --git a/src/jvm/clojure/lang/StringSeq.java b/src/jvm/clojure/lang/StringSeq.java index bcb269d5dc..dbad70810e 100644 --- a/src/jvm/clojure/lang/StringSeq.java +++ b/src/jvm/clojure/lang/StringSeq.java @@ -12,7 +12,13 @@ package clojure.lang; -public class StringSeq extends ASeq implements IndexedSeq{ +import java.util.Iterator; +import java.util.NoSuchElementException; + +public class StringSeq extends ASeq implements IndexedSeq,IDrop,IReduceInit{ + +private static final long serialVersionUID = 7975525539139301753L; + public final CharSequence s; public final int i; @@ -51,4 +57,24 @@ public int index(){ public int count(){ return s.length() - i; } + +public Sequential drop(int n) { + int ii = i + n; + if (ii < s.length()) { + return new StringSeq(null, s, ii); + } else { + return null; + } +} + +public Object reduce(IFn f, Object start) { + Object acc = start; + for(int ii=i; ii < s.length(); ii++) { + acc = f.invoke(acc, s.charAt(ii)); + if(RT.isReduced(acc)) + return ((IDeref)acc).deref(); + } + return acc; +} + } diff --git a/src/jvm/clojure/lang/Symbol.java b/src/jvm/clojure/lang/Symbol.java index 630ef1bb4d..5957bbebbb 100644 --- a/src/jvm/clojure/lang/Symbol.java +++ b/src/jvm/clojure/lang/Symbol.java @@ -17,6 +17,9 @@ public class Symbol extends AFn implements IObj, Comparable, Named, Serializable, IHashEq{ + +private static final long serialVersionUID = 1191039485148212259L; + final String ns; final String name; private int _hasheq; diff --git a/src/jvm/clojure/lang/Util.java b/src/jvm/clojure/lang/Util.java index ff0e338331..11647f800c 100644 --- a/src/jvm/clojure/lang/Util.java +++ b/src/jvm/clojure/lang/Util.java @@ -256,5 +256,27 @@ static public Object loadWithClass(String scriptbase, Class loadFrom) throws } } +static boolean isPosDigit(String s) { + if(s.length() != 1) + return false; + char ch = s.charAt(0); + return ch <= '9' && ch >= '1'; +} + +public static Symbol arrayTypeToSymbol(Class c) { + int dim = 0; + Class componentClass = c; + + while(componentClass.isArray()) { + if(++dim > 9) + break; + + componentClass = componentClass.getComponentType(); + } + if (dim <= 9 && dim >= 1) + return Symbol.intern(componentClass.getName(), Integer.toString(dim)); + else + return Symbol.intern(null, c.getName()); +} } diff --git a/src/jvm/clojure/lang/Var.java b/src/jvm/clojure/lang/Var.java index 787661926c..96f0cee635 100644 --- a/src/jvm/clojure/lang/Var.java +++ b/src/jvm/clojure/lang/Var.java @@ -19,6 +19,8 @@ public final class Var extends ARef implements IFn, IRef, Settable, Serializable{ +private static final long serialVersionUID = 8368961370796295279L; + static class TBox{ volatile Object val; diff --git a/test/clojure/test_clojure/array_symbols.clj b/test/clojure/test_clojure/array_symbols.clj new file mode 100644 index 0000000000..7fb98743e3 --- /dev/null +++ b/test/clojure/test_clojure/array_symbols.clj @@ -0,0 +1,81 @@ +; 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.array-symbols + (:use clojure.test) + (:require [clojure.test-helper :as util])) + +(set! *warn-on-reflection* true) + +(deftest test-array-symbols + (is (= 'java.lang.String/1 (read-string "java.lang.String/1"))) + (is (= 'String/1 (read-string "String/1"))) + (is (= 'int/2 (read-string "int/2"))) + (testing "array symbol resolution" + (are [str-repr klass] (= (Class/forName str-repr) klass) + "[Z" (resolve 'boolean/1) + "[B" (resolve 'byte/1) + "[C" (resolve 'char/1) + "[S" (resolve 'short/1) + "[F" (resolve 'float/1) + "[D" (resolve 'double/1) + "[I" (resolve 'int/1) + "[J" (resolve 'long/1) + "[[J" (resolve 'long/2) + "[Ljava.lang.Object;" (resolve 'Object/1) + "[Ljava.lang.String;" (resolve 'String/1) + "[[Ljava.lang.String;" (resolve 'String/2)) + (is (thrown? ClassNotFoundException (resolve 'ThisIsNotAClassThatCouldBeFound138/2))) + (is (thrown? ClassNotFoundException (resolve 'foo.bar.ThisIsNotAClassThatCouldBeFound138/2)))) + (testing "array hints" + (util/should-not-reflect + (let [^long/1 a (long-array [1 2 3 4 99 100])] + (java.util.Arrays/binarySearch a 99)))) + (testing "syntax quote" + (is (= `byte/1 'byte/1)) + (is (= `byte/9 'byte/9)) + (is (= `java.util.UUID/1 'java.util.UUID/1)) + (is (= `String/1 'java.lang.String/1))) + (testing "resolution" + (is (= (eval 'long/1) (class (make-array Long/TYPE 0)))) + (is (= (resolve 'long/1) (class (make-array Long/TYPE 0)))) + (is (= (resolve 'String/1) (class (make-array String 0)))) + (is (= (resolve 'java.lang.String/1) (class (make-array String 0)))) + (is (= (resolve 'java.util.UUID/1) (class (make-array java.util.UUID 0)))) + (is (= (resolve 'String/2) + (class (into-array (class (make-array String 0)) [(into-array String ["a" "b"])]))))) + (testing "value position" + (is (= (class (make-array String 0)) String/1)) + (is (= [(class (make-array String 0))] [String/1]))) + (testing "printing" + (is (= "long/1" (print-str long/1))) + (is (= "byte/1" (print-str (class (make-array Byte/TYPE 0))))) + (is (= "java.lang.String/2" (print-str String/2))) + (is (= "[[java.lang.String/2]]" (print-str [[String/2]]))) + (is (= "java.util.UUID/4" (print-str java.util.UUID/4))) + (is (= "[[[[[[[[[[Ljava.lang.Object;" (print-str (Class/forName "[[[[[[[[[[Ljava.lang.Object;")))) + (is (= "java.lang.Object/9" (print-str (Class/forName "[[[[[[[[[Ljava.lang.Object;"))))) + (testing "error conditions" + (is (thrown? Exception (read-string "String/1:"))) + (is (thrown? Exception (read-string "String/0"))) + (is (thrown? Exception (read-string "String/42"))) + (is (thrown? Exception (eval '(deftype Foo/2 [a])))))) + +(definterface ArrayClassSymbolFoo (^String/1 bar [])) +(definterface ArrayClassSymbolFooAsHint (^ArrayClassSymbolFoo/1 baz [])) + +(deftest test-definterface-acs + (testing "definterface" + (let [obj (reify ArrayClassSymbolFoo (bar [this] (into-array String ["a"])))] + (is (= ["a"] (seq (.bar obj))))) + (let [obj (reify ArrayClassSymbolFooAsHint + (baz [this] + (into-array ArrayClassSymbolFoo [(reify ArrayClassSymbolFoo + (bar [this] (into-array String ["a"])))])))] + (is (= ["a"] (let [^ArrayClassSymbolFoo acsf (first (.baz obj))] + (seq (.bar acsf)))))))) diff --git a/test/clojure/test_clojure/atoms.clj b/test/clojure/test_clojure/atoms.clj index f9ecadcc56..875f26d60f 100644 --- a/test/clojure/test_clojure/atoms.clj +++ b/test/clojure/test_clojure/atoms.clj @@ -42,3 +42,22 @@ (deftest reset-on-deref-reset-equality (let [a (atom :usual-value)] (is (= :usual-value (reset! a (first (reset-vals! a :almost-never-seen-value))))))) + +(deftest atoms-are-suppliers + (let [a (atom 10)] + (is (instance? java.util.function.Supplier a)) + (is (= 10 (.get ^java.util.function.Supplier a))) + (swap! a inc) + (is (= 11 (.get ^java.util.function.Supplier a))) + + (is (instance? java.util.function.IntSupplier a)) + (is (= 11 (.getAsInt a))) + + (is (instance? java.util.function.LongSupplier a)) + (is (= 11 (.getAsLong a))) + + (is (instance? java.util.function.BooleanSupplier a)) + (is (true? (.getAsBoolean a))) + + (is (instance? java.util.function.DoubleSupplier a)) + (is (= 11.0 (.getAsDouble a))))) \ No newline at end of file diff --git a/test/clojure/test_clojure/clearing.clj b/test/clojure/test_clojure/clearing.clj new file mode 100644 index 0000000000..04e187a74c --- /dev/null +++ b/test/clojure/test_clojure/clearing.clj @@ -0,0 +1,111 @@ +; 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.clearing + (:import + [java.lang.reflect Field]) + (:require + [clojure.string :as str] + [clojure.test :refer :all])) + +(set! *warn-on-reflection* true) + +(defn fields + [o] + (.getDeclaredFields (class o))) + +(defn primitive? + [^Field field] + (.isPrimitive (.getType field))) + +(defn special-fn-field? + [^String field-name] + (or (= field-name "__meta") + (str/starts-with? field-name "__cached_class__") + (str/starts-with? field-name "const__") + (str/ends-with? field-name "__"))) + +(defn clearable-closed-overs + [fobj] + (->> (fields fobj) + (remove primitive?) ;; can't clear primitives + (remove #(special-fn-field? (.getName ^Field %))))) + +(defn private-field-value [^Object obj ^Field field] + (. field (setAccessible true)) + (. field (get obj))) + +;; Check whether all non-primitive closed-overs in a function are nil +(defn cleared? + [fobj] + (every? #(nil? (private-field-value fobj %)) (clearable-closed-overs fobj))) + +;; --- + +;; After invocation, check all closed-over non-primitive fields in a :once fn + +(defn check-clear + [f] + (is (not (cleared? f))) + (f) + (cleared? f)) + +(deftest test-clearing + (let [x :a] + ;; base case + (is (check-clear (^{:once true} fn* [] x))) + + ;; conditional above fn + (when true + (is (check-clear (^{:once true} fn* [] x)))) + (case x + :a (is (check-clear (^{:once true} fn* [] x)))) + + ;; loop above fn + (loop [] + (is (check-clear (^{:once true} fn* [] x)))) + + ;; conditional below fn + (is (check-clear (^{:once true} fn* [] (when true x)))) + + ;; loop below fn + (is (not (check-clear (^{:once true} fn* [] (loop [] x))))) + (is (not (check-clear (^{:once true} fn* [] (loop [] x) nil)))) + + ;; recur in :once below fn + (is (not (check-clear (^{:once true} fn* [] (if false (recur) x))))) + )) + +(deftest test-nested + (let [x :a] + ;; nested fns + (let [inner (^{:once true} fn* [] x) + outer (fn* [] inner)] + (is (not (check-clear outer))) ;; outer not :once + (is (check-clear inner))) + + (let [inner (^{:once true} fn* [] x) + outer (^{:once true} fn* [] inner)] + (is (check-clear outer)) + (is (check-clear inner))) + + (let [inner (^{:once true} fn* [] x) + middle (^{:once true} fn* [] inner) + outer (^{:once true} fn* [] middle)] + (is (check-clear outer)) + (is (check-clear middle)) + (is (check-clear inner))))) + +;; Repro from CLJ-2145 +(defn consume [x] (doseq [_ x] _)) +(defn call-and-keep [f] (f) f) +(defn repro [x] + (if true (call-and-keep (^:once fn* [] (consume x))))) +(deftest CLJ-2145-repro + (let [f (repro (range 100))] ;; 1e9 to exhaust + (is (cleared? f)))) \ No newline at end of file diff --git a/test/clojure/test_clojure/clojure_walk.clj b/test/clojure/test_clojure/clojure_walk.clj index 6c6f866332..fa2f3e6492 100644 --- a/test/clojure/test_clojure/clojure_walk.clj +++ b/test/clojure/test_clojure/clojure_walk.clj @@ -63,3 +63,14 @@ (let [coll [:html {:a ["b" 1]} ""] f (fn [e] (if (and (vector? e) (not (map-entry? e))) (apply list e) e))] (is (= (list :html {:a (list "b" 1)} "") (w/postwalk f coll))))) + +(defrecord RM [a]) +(deftest retain-meta + (let [m {:foo true}] + (are [o] (= m (meta (w/postwalk identity (with-meta o m)))) + '(1 2) + [1 2] + #{1 2} + {1 2} + (map inc (range 3)) + (->RM 1)))) \ No newline at end of file diff --git a/test/clojure/test_clojure/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/compilation.clj b/test/clojure/test_clojure/compilation.clj index 8a5fd84233..61c6905622 100644 --- a/test/clojure/test_clojure/compilation.clj +++ b/test/clojure/test_clojure/compilation.clj @@ -438,3 +438,16 @@ (is (= 42 (compilation.JDK8InterfaceMethods/staticMethod0 42))) (is (= "test" (compilation.JDK8InterfaceMethods/staticMethod1 "test"))) (is (= 1 (if (compilation.JDK8InterfaceMethods/staticMethod2 true) 1 2))))) + +(deftest CLJ-2580 + (testing "CLJ-2580 Correctly calculate exit branches of case" + (is (zero? (let [d (case nil :x nil 0)] d))) + (is (nil? (let [d (case nil :x 0 nil)] d))))) + +(deftest CLJ-2945 + (testing "CLJ-2945 Reify form and obj meta" + (are [m x] (= (set (keys m)) x) + (meta (reify clojure.lang.ILookup)) #{} + (meta ^:foo (reify clojure.lang.ILookup)) #{:foo} + (meta (macroexpand-1 '(reify clojure.lang.ILookup))) #{:line :column} + (meta (macroexpand-1 '^:foo (reify clojure.lang.ILookup))) #{:line :column :foo}))) 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 580e30b073..854560466b 100644 --- a/test/clojure/test_clojure/data_structures.clj +++ b/test/clojure/test_clojure/data_structures.clj @@ -14,7 +14,8 @@ [clojure.test.generative :exclude (is)]) (:require [clojure.test-clojure.generators :as cgen] [clojure.data.generators :as gen] - [clojure.string :as string])) + [clojure.string :as string]) + (:import [java.util Collection])) ;; *** Helper functions *** @@ -1107,7 +1108,11 @@ (defn is-same-collection [a b] (let [msg (format "(class a)=%s (class b)=%s a=%s b=%s" (.getName (class a)) (.getName (class b)) a b)] - (is (= (count a) (count b) (.size a) (.size b)) msg) + (is (= (count a) (count b)) msg) + (when (instance? Collection a) + (is (= (count a) (.size a)) msg)) + (when (instance? Collection b) + (is (= (count b) (.size b)) msg)) (is (= a b) msg) (is (= b a) msg) (is (.equals ^Object a b) msg) @@ -1133,7 +1138,13 @@ (sequence (map identity) [-3 :a "7th"]) ]] (doseq [c1 colls1, c2 colls1] (is-same-collection c1 c2))) - (is-same-collection [-3 1 7] (vector-of :long -3 1 7))) + (let [long-colls [ [2 3 4] + '(2 3 4) + (vector-of :long 2 3 4) + (seq (vector-of :long 2 3 4)) + (range 2 5)]] + (doseq [c1 long-colls, c2 long-colls] + (is-same-collection c1 c2)))) (defn case-indendent-string-cmp [s1 s2] (compare (string/lower-case s1) (string/lower-case s2))) @@ -1319,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/delays.clj b/test/clojure/test_clojure/delays.clj index 0a2a1c99ff..322feb6e49 100644 --- a/test/clojure/test_clojure/delays.clj +++ b/test/clojure/test_clojure/delays.clj @@ -66,3 +66,24 @@ (.await barrier) (is (instance? Exception (try-call))) (is (identical? (try-call) (try-call))))) + +(deftest delays-are-suppliers + (let [dt (delay true) + df (delay false) + dn (delay nil) + di (delay 100)] + + (is (instance? java.util.function.Supplier dt)) + (is (instance? java.util.function.BooleanSupplier dt)) + (is (true? (.get dt))) + (is (true? (.getAsBoolean dt))) + (is (false? (.getAsBoolean df))) + (is (false? (.getAsBoolean dn))) + + (is (instance? java.util.function.Supplier di)) + (is (instance? java.util.function.IntSupplier di)) + (is (instance? java.util.function.LongSupplier di)) + (is (= 100 (.get ^java.util.function.Supplier di))) + (is (= 100 (.getAsInt ^java.util.function.IntSupplier di))) + (is (= 100 (.getAsLong ^java.util.function.LongSupplier di))) + (is (= 100.0 (.getAsDouble ^java.util.function.DoubleSupplier di))))) \ No newline at end of file diff --git a/test/clojure/test_clojure/errors.clj b/test/clojure/test_clojure/errors.clj index cebe0392e3..0ee958786d 100644 --- a/test/clojure/test_clojure/errors.clj +++ b/test/clojure/test_clojure/errors.clj @@ -107,9 +107,9 @@ (with-out-str (pr t)) (catch Throwable t (is nil))))))) -(deftest ex-info-disallows-nil-data - (is (thrown? IllegalArgumentException (ex-info "message" nil))) - (is (thrown? IllegalArgumentException (ex-info "message" nil (Throwable. "cause"))))) +(deftest ex-info-allows-nil-data + (is (= {} (ex-data (ex-info "message" nil)))) + (is (= {} (ex-data (ex-info "message" nil (Throwable. "cause")))))) (deftest ex-info-arities-construct-equivalent-exceptions (let [ex1 (ex-info "message" {:foo "bar"}) diff --git a/test/clojure/test_clojure/genclass.clj b/test/clojure/test_clojure/genclass.clj index 18e808dc11..48ad866365 100644 --- a/test/clojure/test_clojure/genclass.clj +++ b/test/clojure/test_clojure/genclass.clj @@ -149,3 +149,13 @@ (visitSource [source debug] (.append sourceFile source)))] (.accept classReader sourceVisitor 0) (is (= "examples.clj" (str sourceFile))))) + +(deftest array-descriptors->class + (are [descr c] (= (#'clojure.core/the-class descr) c) + "[Ljava.util.UUID;" java.util.UUID/1 + 'String java.lang.String + 'String/1 java.lang.String/1 + 'java.util.UUID java.util.UUID + 'java.util.UUID/2 java.util.UUID/2 + 'int/1 int/1 + 'boolean/9 boolean/9)) diff --git a/test/clojure/test_clojure/generated_all_fi_adapters_in_let.clj b/test/clojure/test_clojure/generated_all_fi_adapters_in_let.clj new file mode 100644 index 0000000000..4bac4914b9 --- /dev/null +++ b/test/clojure/test_clojure/generated_all_fi_adapters_in_let.clj @@ -0,0 +1,305 @@ + +(ns clojure.test-clojure.generated-all-fi-adapters-in-let + (:use clojure.test) + (:require [clojure.string :as str]) + (:import (clojure.test AdapterExerciser AdapterExerciser$L +AdapterExerciser$I +AdapterExerciser$S +AdapterExerciser$B +AdapterExerciser$D +AdapterExerciser$F +AdapterExerciser$O +AdapterExerciser$LL +AdapterExerciser$DL +AdapterExerciser$OL +AdapterExerciser$LI +AdapterExerciser$DI +AdapterExerciser$OI +AdapterExerciser$LS +AdapterExerciser$DS +AdapterExerciser$OS +AdapterExerciser$LB +AdapterExerciser$DB +AdapterExerciser$OB +AdapterExerciser$LD +AdapterExerciser$DD +AdapterExerciser$OD +AdapterExerciser$LF +AdapterExerciser$DF +AdapterExerciser$OF +AdapterExerciser$LO +AdapterExerciser$DO +AdapterExerciser$OO +AdapterExerciser$LLL +AdapterExerciser$LOL +AdapterExerciser$OLL +AdapterExerciser$DDL +AdapterExerciser$LDL +AdapterExerciser$DLL +AdapterExerciser$OOL +AdapterExerciser$ODL +AdapterExerciser$DOL +AdapterExerciser$LLI +AdapterExerciser$LOI +AdapterExerciser$OLI +AdapterExerciser$DDI +AdapterExerciser$LDI +AdapterExerciser$DLI +AdapterExerciser$OOI +AdapterExerciser$ODI +AdapterExerciser$DOI +AdapterExerciser$LLS +AdapterExerciser$LOS +AdapterExerciser$OLS +AdapterExerciser$DDS +AdapterExerciser$LDS +AdapterExerciser$DLS +AdapterExerciser$OOS +AdapterExerciser$ODS +AdapterExerciser$DOS +AdapterExerciser$LLB +AdapterExerciser$LOB +AdapterExerciser$OLB +AdapterExerciser$DDB +AdapterExerciser$LDB +AdapterExerciser$DLB +AdapterExerciser$OOB +AdapterExerciser$ODB +AdapterExerciser$DOB +AdapterExerciser$LLD +AdapterExerciser$LOD +AdapterExerciser$OLD +AdapterExerciser$DDD +AdapterExerciser$LDD +AdapterExerciser$DLD +AdapterExerciser$OOD +AdapterExerciser$ODD +AdapterExerciser$DOD +AdapterExerciser$LLF +AdapterExerciser$LOF +AdapterExerciser$OLF +AdapterExerciser$DDF +AdapterExerciser$LDF +AdapterExerciser$DLF +AdapterExerciser$OOF +AdapterExerciser$ODF +AdapterExerciser$DOF +AdapterExerciser$LLO +AdapterExerciser$LOO +AdapterExerciser$OLO +AdapterExerciser$DDO +AdapterExerciser$LDO +AdapterExerciser$DLO +AdapterExerciser$OOO +AdapterExerciser$ODO +AdapterExerciser$DOO +AdapterExerciser$OOOO +AdapterExerciser$OOOOO +AdapterExerciser$OOOOOO +AdapterExerciser$OOOOOOO +AdapterExerciser$OOOOOOOO +AdapterExerciser$OOOOOOOOO +AdapterExerciser$OOOOOOOOOO +AdapterExerciser$OOOOOOOOOOO +))) + + (deftest test-all-fi-adapters-in-let + (let [^AdapterExerciser exerciser (AdapterExerciser.) + ^AdapterExerciser$L Ladapter (fn [] (long 1)) + ^AdapterExerciser$I Iadapter (fn [] 1) + ^AdapterExerciser$S Sadapter (fn [] (short 1)) + ^AdapterExerciser$B Badapter (fn [] (byte 1)) + ^AdapterExerciser$D Dadapter (fn [] (double 1)) + ^AdapterExerciser$F Fadapter (fn [] (float 1)) + ^AdapterExerciser$O Oadapter (fn [] exerciser) + ^AdapterExerciser$LL LLadapter (fn [^long a] (long 1)) + ^AdapterExerciser$DL DLadapter (fn [^double a] (long 1)) + ^AdapterExerciser$OL OLadapter (fn [^AdapterExerciser a] (long 1)) + ^AdapterExerciser$LI LIadapter (fn [^long a] 1) + ^AdapterExerciser$DI DIadapter (fn [^double a] 1) + ^AdapterExerciser$OI OIadapter (fn [^AdapterExerciser a] 1) + ^AdapterExerciser$LS LSadapter (fn [^long a] (short 1)) + ^AdapterExerciser$DS DSadapter (fn [^double a] (short 1)) + ^AdapterExerciser$OS OSadapter (fn [^AdapterExerciser a] (short 1)) + ^AdapterExerciser$LB LBadapter (fn [^long a] (byte 1)) + ^AdapterExerciser$DB DBadapter (fn [^double a] (byte 1)) + ^AdapterExerciser$OB OBadapter (fn [^AdapterExerciser a] (byte 1)) + ^AdapterExerciser$LD LDadapter (fn [^long a] (double 1)) + ^AdapterExerciser$DD DDadapter (fn [^double a] (double 1)) + ^AdapterExerciser$OD ODadapter (fn [^AdapterExerciser a] (double 1)) + ^AdapterExerciser$LF LFadapter (fn [^long a] (float 1)) + ^AdapterExerciser$DF DFadapter (fn [^double a] (float 1)) + ^AdapterExerciser$OF OFadapter (fn [^AdapterExerciser a] (float 1)) + ^AdapterExerciser$LO LOadapter (fn [^long a] exerciser) + ^AdapterExerciser$DO DOadapter (fn [^double a] exerciser) + ^AdapterExerciser$OO OOadapter (fn [^AdapterExerciser a] exerciser) + ^AdapterExerciser$LLL LLLadapter (fn [^long a ^long b] (long 1)) + ^AdapterExerciser$LOL LOLadapter (fn [^long a ^AdapterExerciser b] (long 1)) + ^AdapterExerciser$OLL OLLadapter (fn [^AdapterExerciser a ^long b] (long 1)) + ^AdapterExerciser$DDL DDLadapter (fn [^double a ^double b] (long 1)) + ^AdapterExerciser$LDL LDLadapter (fn [^long a ^double b] (long 1)) + ^AdapterExerciser$DLL DLLadapter (fn [^double a ^long b] (long 1)) + ^AdapterExerciser$OOL OOLadapter (fn [^AdapterExerciser a ^AdapterExerciser b] (long 1)) + ^AdapterExerciser$ODL ODLadapter (fn [^AdapterExerciser a ^double b] (long 1)) + ^AdapterExerciser$DOL DOLadapter (fn [^double a ^AdapterExerciser b] (long 1)) + ^AdapterExerciser$LLI LLIadapter (fn [^long a ^long b] 1) + ^AdapterExerciser$LOI LOIadapter (fn [^long a ^AdapterExerciser b] 1) + ^AdapterExerciser$OLI OLIadapter (fn [^AdapterExerciser a ^long b] 1) + ^AdapterExerciser$DDI DDIadapter (fn [^double a ^double b] 1) + ^AdapterExerciser$LDI LDIadapter (fn [^long a ^double b] 1) + ^AdapterExerciser$DLI DLIadapter (fn [^double a ^long b] 1) + ^AdapterExerciser$OOI OOIadapter (fn [^AdapterExerciser a ^AdapterExerciser b] 1) + ^AdapterExerciser$ODI ODIadapter (fn [^AdapterExerciser a ^double b] 1) + ^AdapterExerciser$DOI DOIadapter (fn [^double a ^AdapterExerciser b] 1) + ^AdapterExerciser$LLS LLSadapter (fn [^long a ^long b] (short 1)) + ^AdapterExerciser$LOS LOSadapter (fn [^long a ^AdapterExerciser b] (short 1)) + ^AdapterExerciser$OLS OLSadapter (fn [^AdapterExerciser a ^long b] (short 1)) + ^AdapterExerciser$DDS DDSadapter (fn [^double a ^double b] (short 1)) + ^AdapterExerciser$LDS LDSadapter (fn [^long a ^double b] (short 1)) + ^AdapterExerciser$DLS DLSadapter (fn [^double a ^long b] (short 1)) + ^AdapterExerciser$OOS OOSadapter (fn [^AdapterExerciser a ^AdapterExerciser b] (short 1)) + ^AdapterExerciser$ODS ODSadapter (fn [^AdapterExerciser a ^double b] (short 1)) + ^AdapterExerciser$DOS DOSadapter (fn [^double a ^AdapterExerciser b] (short 1)) + ^AdapterExerciser$LLB LLBadapter (fn [^long a ^long b] (byte 1)) + ^AdapterExerciser$LOB LOBadapter (fn [^long a ^AdapterExerciser b] (byte 1)) + ^AdapterExerciser$OLB OLBadapter (fn [^AdapterExerciser a ^long b] (byte 1)) + ^AdapterExerciser$DDB DDBadapter (fn [^double a ^double b] (byte 1)) + ^AdapterExerciser$LDB LDBadapter (fn [^long a ^double b] (byte 1)) + ^AdapterExerciser$DLB DLBadapter (fn [^double a ^long b] (byte 1)) + ^AdapterExerciser$OOB OOBadapter (fn [^AdapterExerciser a ^AdapterExerciser b] (byte 1)) + ^AdapterExerciser$ODB ODBadapter (fn [^AdapterExerciser a ^double b] (byte 1)) + ^AdapterExerciser$DOB DOBadapter (fn [^double a ^AdapterExerciser b] (byte 1)) + ^AdapterExerciser$LLD LLDadapter (fn [^long a ^long b] (double 1)) + ^AdapterExerciser$LOD LODadapter (fn [^long a ^AdapterExerciser b] (double 1)) + ^AdapterExerciser$OLD OLDadapter (fn [^AdapterExerciser a ^long b] (double 1)) + ^AdapterExerciser$DDD DDDadapter (fn [^double a ^double b] (double 1)) + ^AdapterExerciser$LDD LDDadapter (fn [^long a ^double b] (double 1)) + ^AdapterExerciser$DLD DLDadapter (fn [^double a ^long b] (double 1)) + ^AdapterExerciser$OOD OODadapter (fn [^AdapterExerciser a ^AdapterExerciser b] (double 1)) + ^AdapterExerciser$ODD ODDadapter (fn [^AdapterExerciser a ^double b] (double 1)) + ^AdapterExerciser$DOD DODadapter (fn [^double a ^AdapterExerciser b] (double 1)) + ^AdapterExerciser$LLF LLFadapter (fn [^long a ^long b] (float 1)) + ^AdapterExerciser$LOF LOFadapter (fn [^long a ^AdapterExerciser b] (float 1)) + ^AdapterExerciser$OLF OLFadapter (fn [^AdapterExerciser a ^long b] (float 1)) + ^AdapterExerciser$DDF DDFadapter (fn [^double a ^double b] (float 1)) + ^AdapterExerciser$LDF LDFadapter (fn [^long a ^double b] (float 1)) + ^AdapterExerciser$DLF DLFadapter (fn [^double a ^long b] (float 1)) + ^AdapterExerciser$OOF OOFadapter (fn [^AdapterExerciser a ^AdapterExerciser b] (float 1)) + ^AdapterExerciser$ODF ODFadapter (fn [^AdapterExerciser a ^double b] (float 1)) + ^AdapterExerciser$DOF DOFadapter (fn [^double a ^AdapterExerciser b] (float 1)) + ^AdapterExerciser$LLO LLOadapter (fn [^long a ^long b] exerciser) + ^AdapterExerciser$LOO LOOadapter (fn [^long a ^AdapterExerciser b] exerciser) + ^AdapterExerciser$OLO OLOadapter (fn [^AdapterExerciser a ^long b] exerciser) + ^AdapterExerciser$DDO DDOadapter (fn [^double a ^double b] exerciser) + ^AdapterExerciser$LDO LDOadapter (fn [^long a ^double b] exerciser) + ^AdapterExerciser$DLO DLOadapter (fn [^double a ^long b] exerciser) + ^AdapterExerciser$OOO OOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b] exerciser) + ^AdapterExerciser$ODO ODOadapter (fn [^AdapterExerciser a ^double b] exerciser) + ^AdapterExerciser$DOO DOOadapter (fn [^double a ^AdapterExerciser b] exerciser) + ^AdapterExerciser$OOOO OOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c] exerciser) + ^AdapterExerciser$OOOOO OOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d] exerciser) + ^AdapterExerciser$OOOOOO OOOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d ^AdapterExerciser e] exerciser) + ^AdapterExerciser$OOOOOOO OOOOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d ^AdapterExerciser e ^AdapterExerciser f] exerciser) + ^AdapterExerciser$OOOOOOOO OOOOOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d ^AdapterExerciser e ^AdapterExerciser f ^AdapterExerciser g] exerciser) + ^AdapterExerciser$OOOOOOOOO OOOOOOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d ^AdapterExerciser e ^AdapterExerciser f ^AdapterExerciser g ^AdapterExerciser h] exerciser) + ^AdapterExerciser$OOOOOOOOOO OOOOOOOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d ^AdapterExerciser e ^AdapterExerciser f ^AdapterExerciser g ^AdapterExerciser h ^AdapterExerciser i] exerciser) + ^AdapterExerciser$OOOOOOOOOOO OOOOOOOOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d ^AdapterExerciser e ^AdapterExerciser f ^AdapterExerciser g ^AdapterExerciser h ^AdapterExerciser i ^AdapterExerciser j] exerciser)] + (is (= (.takesRetL Ladapter ) (long 1))) + (is (= (.takesRetI Iadapter ) 1)) + (is (= (.takesRetS Sadapter ) (short 1))) + (is (= (.takesRetB Badapter ) (byte 1))) + (is (= (.takesRetD Dadapter ) (double 1))) + (is (= (.takesRetF Fadapter ) (float 1))) + (is (= (.takesRetO Oadapter ) exerciser)) + (is (= (.takesLRetL LLadapter (long 1)) (long 1))) + (is (= (.takesDRetL DLadapter (double 1)) (long 1))) + (is (= (.takesORetL OLadapter exerciser) (long 1))) + (is (= (.takesLRetI LIadapter (long 1)) 1)) + (is (= (.takesDRetI DIadapter (double 1)) 1)) + (is (= (.takesORetI OIadapter exerciser) 1)) + (is (= (.takesLRetS LSadapter (long 1)) (short 1))) + (is (= (.takesDRetS DSadapter (double 1)) (short 1))) + (is (= (.takesORetS OSadapter exerciser) (short 1))) + (is (= (.takesLRetB LBadapter (long 1)) (byte 1))) + (is (= (.takesDRetB DBadapter (double 1)) (byte 1))) + (is (= (.takesORetB OBadapter exerciser) (byte 1))) + (is (= (.takesLRetD LDadapter (long 1)) (double 1))) + (is (= (.takesDRetD DDadapter (double 1)) (double 1))) + (is (= (.takesORetD ODadapter exerciser) (double 1))) + (is (= (.takesLRetF LFadapter (long 1)) (float 1))) + (is (= (.takesDRetF DFadapter (double 1)) (float 1))) + (is (= (.takesORetF OFadapter exerciser) (float 1))) + (is (= (.takesLRetO LOadapter (long 1)) exerciser)) + (is (= (.takesDRetO DOadapter (double 1)) exerciser)) + (is (= (.takesORetO OOadapter exerciser) exerciser)) + (is (= (.takesLLRetL LLLadapter (long 1) (long 1)) (long 1))) + (is (= (.takesLORetL LOLadapter (long 1) exerciser) (long 1))) + (is (= (.takesOLRetL OLLadapter exerciser (long 1)) (long 1))) + (is (= (.takesDDRetL DDLadapter (double 1) (double 1)) (long 1))) + (is (= (.takesLDRetL LDLadapter (long 1) (double 1)) (long 1))) + (is (= (.takesDLRetL DLLadapter (double 1) (long 1)) (long 1))) + (is (= (.takesOORetL OOLadapter exerciser exerciser) (long 1))) + (is (= (.takesODRetL ODLadapter exerciser (double 1)) (long 1))) + (is (= (.takesDORetL DOLadapter (double 1) exerciser) (long 1))) + (is (= (.takesLLRetI LLIadapter (long 1) (long 1)) 1)) + (is (= (.takesLORetI LOIadapter (long 1) exerciser) 1)) + (is (= (.takesOLRetI OLIadapter exerciser (long 1)) 1)) + (is (= (.takesDDRetI DDIadapter (double 1) (double 1)) 1)) + (is (= (.takesLDRetI LDIadapter (long 1) (double 1)) 1)) + (is (= (.takesDLRetI DLIadapter (double 1) (long 1)) 1)) + (is (= (.takesOORetI OOIadapter exerciser exerciser) 1)) + (is (= (.takesODRetI ODIadapter exerciser (double 1)) 1)) + (is (= (.takesDORetI DOIadapter (double 1) exerciser) 1)) + (is (= (.takesLLRetS LLSadapter (long 1) (long 1)) (short 1))) + (is (= (.takesLORetS LOSadapter (long 1) exerciser) (short 1))) + (is (= (.takesOLRetS OLSadapter exerciser (long 1)) (short 1))) + (is (= (.takesDDRetS DDSadapter (double 1) (double 1)) (short 1))) + (is (= (.takesLDRetS LDSadapter (long 1) (double 1)) (short 1))) + (is (= (.takesDLRetS DLSadapter (double 1) (long 1)) (short 1))) + (is (= (.takesOORetS OOSadapter exerciser exerciser) (short 1))) + (is (= (.takesODRetS ODSadapter exerciser (double 1)) (short 1))) + (is (= (.takesDORetS DOSadapter (double 1) exerciser) (short 1))) + (is (= (.takesLLRetB LLBadapter (long 1) (long 1)) (byte 1))) + (is (= (.takesLORetB LOBadapter (long 1) exerciser) (byte 1))) + (is (= (.takesOLRetB OLBadapter exerciser (long 1)) (byte 1))) + (is (= (.takesDDRetB DDBadapter (double 1) (double 1)) (byte 1))) + (is (= (.takesLDRetB LDBadapter (long 1) (double 1)) (byte 1))) + (is (= (.takesDLRetB DLBadapter (double 1) (long 1)) (byte 1))) + (is (= (.takesOORetB OOBadapter exerciser exerciser) (byte 1))) + (is (= (.takesODRetB ODBadapter exerciser (double 1)) (byte 1))) + (is (= (.takesDORetB DOBadapter (double 1) exerciser) (byte 1))) + (is (= (.takesLLRetD LLDadapter (long 1) (long 1)) (double 1))) + (is (= (.takesLORetD LODadapter (long 1) exerciser) (double 1))) + (is (= (.takesOLRetD OLDadapter exerciser (long 1)) (double 1))) + (is (= (.takesDDRetD DDDadapter (double 1) (double 1)) (double 1))) + (is (= (.takesLDRetD LDDadapter (long 1) (double 1)) (double 1))) + (is (= (.takesDLRetD DLDadapter (double 1) (long 1)) (double 1))) + (is (= (.takesOORetD OODadapter exerciser exerciser) (double 1))) + (is (= (.takesODRetD ODDadapter exerciser (double 1)) (double 1))) + (is (= (.takesDORetD DODadapter (double 1) exerciser) (double 1))) + (is (= (.takesLLRetF LLFadapter (long 1) (long 1)) (float 1))) + (is (= (.takesLORetF LOFadapter (long 1) exerciser) (float 1))) + (is (= (.takesOLRetF OLFadapter exerciser (long 1)) (float 1))) + (is (= (.takesDDRetF DDFadapter (double 1) (double 1)) (float 1))) + (is (= (.takesLDRetF LDFadapter (long 1) (double 1)) (float 1))) + (is (= (.takesDLRetF DLFadapter (double 1) (long 1)) (float 1))) + (is (= (.takesOORetF OOFadapter exerciser exerciser) (float 1))) + (is (= (.takesODRetF ODFadapter exerciser (double 1)) (float 1))) + (is (= (.takesDORetF DOFadapter (double 1) exerciser) (float 1))) + (is (= (.takesLLRetO LLOadapter (long 1) (long 1)) exerciser)) + (is (= (.takesLORetO LOOadapter (long 1) exerciser) exerciser)) + (is (= (.takesOLRetO OLOadapter exerciser (long 1)) exerciser)) + (is (= (.takesDDRetO DDOadapter (double 1) (double 1)) exerciser)) + (is (= (.takesLDRetO LDOadapter (long 1) (double 1)) exerciser)) + (is (= (.takesDLRetO DLOadapter (double 1) (long 1)) exerciser)) + (is (= (.takesOORetO OOOadapter exerciser exerciser) exerciser)) + (is (= (.takesODRetO ODOadapter exerciser (double 1)) exerciser)) + (is (= (.takesDORetO DOOadapter (double 1) exerciser) exerciser)) + (is (= (.takesOOORetO OOOOadapter exerciser exerciser exerciser) exerciser)) + (is (= (.takesOOOORetO OOOOOadapter exerciser exerciser exerciser exerciser) exerciser)) + (is (= (.takesOOOOORetO OOOOOOadapter exerciser exerciser exerciser exerciser exerciser) exerciser)) + (is (= (.takesOOOOOORetO OOOOOOOadapter exerciser exerciser exerciser exerciser exerciser exerciser) exerciser)) + (is (= (.takesOOOOOOORetO OOOOOOOOadapter exerciser exerciser exerciser exerciser exerciser exerciser exerciser) exerciser)) + (is (= (.takesOOOOOOOORetO OOOOOOOOOadapter exerciser exerciser exerciser exerciser exerciser exerciser exerciser exerciser) exerciser)) + (is (= (.takesOOOOOOOOORetO OOOOOOOOOOadapter exerciser exerciser exerciser exerciser exerciser exerciser exerciser exerciser exerciser) exerciser)) + (is (= (.takesOOOOOOOOOORetO OOOOOOOOOOOadapter exerciser exerciser exerciser exerciser exerciser exerciser exerciser exerciser exerciser exerciser) exerciser)))) \ No newline at end of file diff --git a/test/clojure/test_clojure/generated_functional_adapters_in_def.clj b/test/clojure/test_clojure/generated_functional_adapters_in_def.clj new file mode 100644 index 0000000000..ba62daa7c7 --- /dev/null +++ b/test/clojure/test_clojure/generated_functional_adapters_in_def.clj @@ -0,0 +1,206 @@ + +(ns clojure.test-clojure.generated-functional-adapters-in-def + (:use clojure.test) + (:require [clojure.string :as str]) + (:import (clojure.test AdapterExerciser))) + + (deftest functional-adapters-in-def + (def exerciser (AdapterExerciser.)) + (def Ladapter (fn [] (long 1))) + (is (= (.methodL ^AdapterExerciser exerciser Ladapter) "L")) + (def Iadapter (fn [] 1)) + (is (= (.methodI ^AdapterExerciser exerciser Iadapter) "I")) + (def Sadapter (fn [] (short 1))) + (is (= (.methodS ^AdapterExerciser exerciser Sadapter) "S")) + (def Badapter (fn [] (byte 1))) + (is (= (.methodB ^AdapterExerciser exerciser Badapter) "B")) + (def Dadapter (fn [] (double 1))) + (is (= (.methodD ^AdapterExerciser exerciser Dadapter) "D")) + (def Fadapter (fn [] (float 1))) + (is (= (.methodF ^AdapterExerciser exerciser Fadapter) "F")) + (def Oadapter (fn [] exerciser)) + (is (= (.methodO ^AdapterExerciser exerciser Oadapter) "O")) + (def LLadapter (fn [^long a] (long 1))) + (is (= (.methodLL ^AdapterExerciser exerciser LLadapter) "LL")) + (def DLadapter (fn [^double a] (long 1))) + (is (= (.methodDL ^AdapterExerciser exerciser DLadapter) "DL")) + (def OLadapter (fn [^AdapterExerciser a] (long 1))) + (is (= (.methodOL ^AdapterExerciser exerciser OLadapter) "OL")) + (def LIadapter (fn [^long a] 1)) + (is (= (.methodLI ^AdapterExerciser exerciser LIadapter) "LI")) + (def DIadapter (fn [^double a] 1)) + (is (= (.methodDI ^AdapterExerciser exerciser DIadapter) "DI")) + (def OIadapter (fn [^AdapterExerciser a] 1)) + (is (= (.methodOI ^AdapterExerciser exerciser OIadapter) "OI")) + (def LSadapter (fn [^long a] (short 1))) + (is (= (.methodLS ^AdapterExerciser exerciser LSadapter) "LS")) + (def DSadapter (fn [^double a] (short 1))) + (is (= (.methodDS ^AdapterExerciser exerciser DSadapter) "DS")) + (def OSadapter (fn [^AdapterExerciser a] (short 1))) + (is (= (.methodOS ^AdapterExerciser exerciser OSadapter) "OS")) + (def LBadapter (fn [^long a] (byte 1))) + (is (= (.methodLB ^AdapterExerciser exerciser LBadapter) "LB")) + (def DBadapter (fn [^double a] (byte 1))) + (is (= (.methodDB ^AdapterExerciser exerciser DBadapter) "DB")) + (def OBadapter (fn [^AdapterExerciser a] (byte 1))) + (is (= (.methodOB ^AdapterExerciser exerciser OBadapter) "OB")) + (def LDadapter (fn [^long a] (double 1))) + (is (= (.methodLD ^AdapterExerciser exerciser LDadapter) "LD")) + (def DDadapter (fn [^double a] (double 1))) + (is (= (.methodDD ^AdapterExerciser exerciser DDadapter) "DD")) + (def ODadapter (fn [^AdapterExerciser a] (double 1))) + (is (= (.methodOD ^AdapterExerciser exerciser ODadapter) "OD")) + (def LFadapter (fn [^long a] (float 1))) + (is (= (.methodLF ^AdapterExerciser exerciser LFadapter) "LF")) + (def DFadapter (fn [^double a] (float 1))) + (is (= (.methodDF ^AdapterExerciser exerciser DFadapter) "DF")) + (def OFadapter (fn [^AdapterExerciser a] (float 1))) + (is (= (.methodOF ^AdapterExerciser exerciser OFadapter) "OF")) + (def LOadapter (fn [^long a] exerciser)) + (is (= (.methodLO ^AdapterExerciser exerciser LOadapter) "LO")) + (def DOadapter (fn [^double a] exerciser)) + (is (= (.methodDO ^AdapterExerciser exerciser DOadapter) "DO")) + (def OOadapter (fn [^AdapterExerciser a] exerciser)) + (is (= (.methodOO ^AdapterExerciser exerciser OOadapter) "OO")) + (def LLLadapter (fn [^long a ^long b] (long 1))) + (is (= (.methodLLL ^AdapterExerciser exerciser LLLadapter) "LLL")) + (def LOLadapter (fn [^long a ^AdapterExerciser b] (long 1))) + (is (= (.methodLOL ^AdapterExerciser exerciser LOLadapter) "LOL")) + (def OLLadapter (fn [^AdapterExerciser a ^long b] (long 1))) + (is (= (.methodOLL ^AdapterExerciser exerciser OLLadapter) "OLL")) + (def DDLadapter (fn [^double a ^double b] (long 1))) + (is (= (.methodDDL ^AdapterExerciser exerciser DDLadapter) "DDL")) + (def LDLadapter (fn [^long a ^double b] (long 1))) + (is (= (.methodLDL ^AdapterExerciser exerciser LDLadapter) "LDL")) + (def DLLadapter (fn [^double a ^long b] (long 1))) + (is (= (.methodDLL ^AdapterExerciser exerciser DLLadapter) "DLL")) + (def OOLadapter (fn [^AdapterExerciser a ^AdapterExerciser b] (long 1))) + (is (= (.methodOOL ^AdapterExerciser exerciser OOLadapter) "OOL")) + (def ODLadapter (fn [^AdapterExerciser a ^double b] (long 1))) + (is (= (.methodODL ^AdapterExerciser exerciser ODLadapter) "ODL")) + (def DOLadapter (fn [^double a ^AdapterExerciser b] (long 1))) + (is (= (.methodDOL ^AdapterExerciser exerciser DOLadapter) "DOL")) + (def LLIadapter (fn [^long a ^long b] 1)) + (is (= (.methodLLI ^AdapterExerciser exerciser LLIadapter) "LLI")) + (def LOIadapter (fn [^long a ^AdapterExerciser b] 1)) + (is (= (.methodLOI ^AdapterExerciser exerciser LOIadapter) "LOI")) + (def OLIadapter (fn [^AdapterExerciser a ^long b] 1)) + (is (= (.methodOLI ^AdapterExerciser exerciser OLIadapter) "OLI")) + (def DDIadapter (fn [^double a ^double b] 1)) + (is (= (.methodDDI ^AdapterExerciser exerciser DDIadapter) "DDI")) + (def LDIadapter (fn [^long a ^double b] 1)) + (is (= (.methodLDI ^AdapterExerciser exerciser LDIadapter) "LDI")) + (def DLIadapter (fn [^double a ^long b] 1)) + (is (= (.methodDLI ^AdapterExerciser exerciser DLIadapter) "DLI")) + (def OOIadapter (fn [^AdapterExerciser a ^AdapterExerciser b] 1)) + (is (= (.methodOOI ^AdapterExerciser exerciser OOIadapter) "OOI")) + (def ODIadapter (fn [^AdapterExerciser a ^double b] 1)) + (is (= (.methodODI ^AdapterExerciser exerciser ODIadapter) "ODI")) + (def DOIadapter (fn [^double a ^AdapterExerciser b] 1)) + (is (= (.methodDOI ^AdapterExerciser exerciser DOIadapter) "DOI")) + (def LLSadapter (fn [^long a ^long b] (short 1))) + (is (= (.methodLLS ^AdapterExerciser exerciser LLSadapter) "LLS")) + (def LOSadapter (fn [^long a ^AdapterExerciser b] (short 1))) + (is (= (.methodLOS ^AdapterExerciser exerciser LOSadapter) "LOS")) + (def OLSadapter (fn [^AdapterExerciser a ^long b] (short 1))) + (is (= (.methodOLS ^AdapterExerciser exerciser OLSadapter) "OLS")) + (def DDSadapter (fn [^double a ^double b] (short 1))) + (is (= (.methodDDS ^AdapterExerciser exerciser DDSadapter) "DDS")) + (def LDSadapter (fn [^long a ^double b] (short 1))) + (is (= (.methodLDS ^AdapterExerciser exerciser LDSadapter) "LDS")) + (def DLSadapter (fn [^double a ^long b] (short 1))) + (is (= (.methodDLS ^AdapterExerciser exerciser DLSadapter) "DLS")) + (def OOSadapter (fn [^AdapterExerciser a ^AdapterExerciser b] (short 1))) + (is (= (.methodOOS ^AdapterExerciser exerciser OOSadapter) "OOS")) + (def ODSadapter (fn [^AdapterExerciser a ^double b] (short 1))) + (is (= (.methodODS ^AdapterExerciser exerciser ODSadapter) "ODS")) + (def DOSadapter (fn [^double a ^AdapterExerciser b] (short 1))) + (is (= (.methodDOS ^AdapterExerciser exerciser DOSadapter) "DOS")) + (def LLBadapter (fn [^long a ^long b] (byte 1))) + (is (= (.methodLLB ^AdapterExerciser exerciser LLBadapter) "LLB")) + (def LOBadapter (fn [^long a ^AdapterExerciser b] (byte 1))) + (is (= (.methodLOB ^AdapterExerciser exerciser LOBadapter) "LOB")) + (def OLBadapter (fn [^AdapterExerciser a ^long b] (byte 1))) + (is (= (.methodOLB ^AdapterExerciser exerciser OLBadapter) "OLB")) + (def DDBadapter (fn [^double a ^double b] (byte 1))) + (is (= (.methodDDB ^AdapterExerciser exerciser DDBadapter) "DDB")) + (def LDBadapter (fn [^long a ^double b] (byte 1))) + (is (= (.methodLDB ^AdapterExerciser exerciser LDBadapter) "LDB")) + (def DLBadapter (fn [^double a ^long b] (byte 1))) + (is (= (.methodDLB ^AdapterExerciser exerciser DLBadapter) "DLB")) + (def OOBadapter (fn [^AdapterExerciser a ^AdapterExerciser b] (byte 1))) + (is (= (.methodOOB ^AdapterExerciser exerciser OOBadapter) "OOB")) + (def ODBadapter (fn [^AdapterExerciser a ^double b] (byte 1))) + (is (= (.methodODB ^AdapterExerciser exerciser ODBadapter) "ODB")) + (def DOBadapter (fn [^double a ^AdapterExerciser b] (byte 1))) + (is (= (.methodDOB ^AdapterExerciser exerciser DOBadapter) "DOB")) + (def LLDadapter (fn [^long a ^long b] (double 1))) + (is (= (.methodLLD ^AdapterExerciser exerciser LLDadapter) "LLD")) + (def LODadapter (fn [^long a ^AdapterExerciser b] (double 1))) + (is (= (.methodLOD ^AdapterExerciser exerciser LODadapter) "LOD")) + (def OLDadapter (fn [^AdapterExerciser a ^long b] (double 1))) + (is (= (.methodOLD ^AdapterExerciser exerciser OLDadapter) "OLD")) + (def DDDadapter (fn [^double a ^double b] (double 1))) + (is (= (.methodDDD ^AdapterExerciser exerciser DDDadapter) "DDD")) + (def LDDadapter (fn [^long a ^double b] (double 1))) + (is (= (.methodLDD ^AdapterExerciser exerciser LDDadapter) "LDD")) + (def DLDadapter (fn [^double a ^long b] (double 1))) + (is (= (.methodDLD ^AdapterExerciser exerciser DLDadapter) "DLD")) + (def OODadapter (fn [^AdapterExerciser a ^AdapterExerciser b] (double 1))) + (is (= (.methodOOD ^AdapterExerciser exerciser OODadapter) "OOD")) + (def ODDadapter (fn [^AdapterExerciser a ^double b] (double 1))) + (is (= (.methodODD ^AdapterExerciser exerciser ODDadapter) "ODD")) + (def DODadapter (fn [^double a ^AdapterExerciser b] (double 1))) + (is (= (.methodDOD ^AdapterExerciser exerciser DODadapter) "DOD")) + (def LLFadapter (fn [^long a ^long b] (float 1))) + (is (= (.methodLLF ^AdapterExerciser exerciser LLFadapter) "LLF")) + (def LOFadapter (fn [^long a ^AdapterExerciser b] (float 1))) + (is (= (.methodLOF ^AdapterExerciser exerciser LOFadapter) "LOF")) + (def OLFadapter (fn [^AdapterExerciser a ^long b] (float 1))) + (is (= (.methodOLF ^AdapterExerciser exerciser OLFadapter) "OLF")) + (def DDFadapter (fn [^double a ^double b] (float 1))) + (is (= (.methodDDF ^AdapterExerciser exerciser DDFadapter) "DDF")) + (def LDFadapter (fn [^long a ^double b] (float 1))) + (is (= (.methodLDF ^AdapterExerciser exerciser LDFadapter) "LDF")) + (def DLFadapter (fn [^double a ^long b] (float 1))) + (is (= (.methodDLF ^AdapterExerciser exerciser DLFadapter) "DLF")) + (def OOFadapter (fn [^AdapterExerciser a ^AdapterExerciser b] (float 1))) + (is (= (.methodOOF ^AdapterExerciser exerciser OOFadapter) "OOF")) + (def ODFadapter (fn [^AdapterExerciser a ^double b] (float 1))) + (is (= (.methodODF ^AdapterExerciser exerciser ODFadapter) "ODF")) + (def DOFadapter (fn [^double a ^AdapterExerciser b] (float 1))) + (is (= (.methodDOF ^AdapterExerciser exerciser DOFadapter) "DOF")) + (def LLOadapter (fn [^long a ^long b] exerciser)) + (is (= (.methodLLO ^AdapterExerciser exerciser LLOadapter) "LLO")) + (def LOOadapter (fn [^long a ^AdapterExerciser b] exerciser)) + (is (= (.methodLOO ^AdapterExerciser exerciser LOOadapter) "LOO")) + (def OLOadapter (fn [^AdapterExerciser a ^long b] exerciser)) + (is (= (.methodOLO ^AdapterExerciser exerciser OLOadapter) "OLO")) + (def DDOadapter (fn [^double a ^double b] exerciser)) + (is (= (.methodDDO ^AdapterExerciser exerciser DDOadapter) "DDO")) + (def LDOadapter (fn [^long a ^double b] exerciser)) + (is (= (.methodLDO ^AdapterExerciser exerciser LDOadapter) "LDO")) + (def DLOadapter (fn [^double a ^long b] exerciser)) + (is (= (.methodDLO ^AdapterExerciser exerciser DLOadapter) "DLO")) + (def OOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b] exerciser)) + (is (= (.methodOOO ^AdapterExerciser exerciser OOOadapter) "OOO")) + (def ODOadapter (fn [^AdapterExerciser a ^double b] exerciser)) + (is (= (.methodODO ^AdapterExerciser exerciser ODOadapter) "ODO")) + (def DOOadapter (fn [^double a ^AdapterExerciser b] exerciser)) + (is (= (.methodDOO ^AdapterExerciser exerciser DOOadapter) "DOO")) + (def OOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c] exerciser)) + (is (= (.methodOOOO ^AdapterExerciser exerciser OOOOadapter) "OOOO")) + (def OOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d] exerciser)) + (is (= (.methodOOOOO ^AdapterExerciser exerciser OOOOOadapter) "OOOOO")) + (def OOOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d ^AdapterExerciser e] exerciser)) + (is (= (.methodOOOOOO ^AdapterExerciser exerciser OOOOOOadapter) "OOOOOO")) + (def OOOOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d ^AdapterExerciser e ^AdapterExerciser f] exerciser)) + (is (= (.methodOOOOOOO ^AdapterExerciser exerciser OOOOOOOadapter) "OOOOOOO")) + (def OOOOOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d ^AdapterExerciser e ^AdapterExerciser f ^AdapterExerciser g] exerciser)) + (is (= (.methodOOOOOOOO ^AdapterExerciser exerciser OOOOOOOOadapter) "OOOOOOOO")) + (def OOOOOOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d ^AdapterExerciser e ^AdapterExerciser f ^AdapterExerciser g ^AdapterExerciser h] exerciser)) + (is (= (.methodOOOOOOOOO ^AdapterExerciser exerciser OOOOOOOOOadapter) "OOOOOOOOO")) + (def OOOOOOOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d ^AdapterExerciser e ^AdapterExerciser f ^AdapterExerciser g ^AdapterExerciser h ^AdapterExerciser i] exerciser)) + (is (= (.methodOOOOOOOOOO ^AdapterExerciser exerciser OOOOOOOOOOadapter) "OOOOOOOOOO")) + (def OOOOOOOOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d ^AdapterExerciser e ^AdapterExerciser f ^AdapterExerciser g ^AdapterExerciser h ^AdapterExerciser i ^AdapterExerciser j] exerciser)) + (is (= (.methodOOOOOOOOOOO ^AdapterExerciser exerciser OOOOOOOOOOOadapter) "OOOOOOOOOOO"))) \ No newline at end of file diff --git a/test/clojure/test_clojure/generated_functional_adapters_in_def_requiring_reflection.clj b/test/clojure/test_clojure/generated_functional_adapters_in_def_requiring_reflection.clj new file mode 100644 index 0000000000..7e6212c3ab --- /dev/null +++ b/test/clojure/test_clojure/generated_functional_adapters_in_def_requiring_reflection.clj @@ -0,0 +1,206 @@ + +(ns clojure.test-clojure.generated-functional-adapters-in-def-requiring-reflection + (:use clojure.test) + (:require [clojure.string :as str]) + (:import (clojure.test AdapterExerciser))) + + (deftest functional-adapters-in-def + (def exerciser (AdapterExerciser.)) + (def Ladapter (fn [] (long 1))) + (is (= (.methodL exerciser Ladapter) "L")) + (def Iadapter (fn [] 1)) + (is (= (.methodI exerciser Iadapter) "I")) + (def Sadapter (fn [] (short 1))) + (is (= (.methodS exerciser Sadapter) "S")) + (def Badapter (fn [] (byte 1))) + (is (= (.methodB exerciser Badapter) "B")) + (def Dadapter (fn [] (double 1))) + (is (= (.methodD exerciser Dadapter) "D")) + (def Fadapter (fn [] (float 1))) + (is (= (.methodF exerciser Fadapter) "F")) + (def Oadapter (fn [] exerciser)) + (is (= (.methodO exerciser Oadapter) "O")) + (def LLadapter (fn [^long a] (long 1))) + (is (= (.methodLL exerciser LLadapter) "LL")) + (def DLadapter (fn [^double a] (long 1))) + (is (= (.methodDL exerciser DLadapter) "DL")) + (def OLadapter (fn [^AdapterExerciser a] (long 1))) + (is (= (.methodOL exerciser OLadapter) "OL")) + (def LIadapter (fn [^long a] 1)) + (is (= (.methodLI exerciser LIadapter) "LI")) + (def DIadapter (fn [^double a] 1)) + (is (= (.methodDI exerciser DIadapter) "DI")) + (def OIadapter (fn [^AdapterExerciser a] 1)) + (is (= (.methodOI exerciser OIadapter) "OI")) + (def LSadapter (fn [^long a] (short 1))) + (is (= (.methodLS exerciser LSadapter) "LS")) + (def DSadapter (fn [^double a] (short 1))) + (is (= (.methodDS exerciser DSadapter) "DS")) + (def OSadapter (fn [^AdapterExerciser a] (short 1))) + (is (= (.methodOS exerciser OSadapter) "OS")) + (def LBadapter (fn [^long a] (byte 1))) + (is (= (.methodLB exerciser LBadapter) "LB")) + (def DBadapter (fn [^double a] (byte 1))) + (is (= (.methodDB exerciser DBadapter) "DB")) + (def OBadapter (fn [^AdapterExerciser a] (byte 1))) + (is (= (.methodOB exerciser OBadapter) "OB")) + (def LDadapter (fn [^long a] (double 1))) + (is (= (.methodLD exerciser LDadapter) "LD")) + (def DDadapter (fn [^double a] (double 1))) + (is (= (.methodDD exerciser DDadapter) "DD")) + (def ODadapter (fn [^AdapterExerciser a] (double 1))) + (is (= (.methodOD exerciser ODadapter) "OD")) + (def LFadapter (fn [^long a] (float 1))) + (is (= (.methodLF exerciser LFadapter) "LF")) + (def DFadapter (fn [^double a] (float 1))) + (is (= (.methodDF exerciser DFadapter) "DF")) + (def OFadapter (fn [^AdapterExerciser a] (float 1))) + (is (= (.methodOF exerciser OFadapter) "OF")) + (def LOadapter (fn [^long a] exerciser)) + (is (= (.methodLO exerciser LOadapter) "LO")) + (def DOadapter (fn [^double a] exerciser)) + (is (= (.methodDO exerciser DOadapter) "DO")) + (def OOadapter (fn [^AdapterExerciser a] exerciser)) + (is (= (.methodOO exerciser OOadapter) "OO")) + (def LLLadapter (fn [^long a ^long b] (long 1))) + (is (= (.methodLLL exerciser LLLadapter) "LLL")) + (def LOLadapter (fn [^long a ^AdapterExerciser b] (long 1))) + (is (= (.methodLOL exerciser LOLadapter) "LOL")) + (def OLLadapter (fn [^AdapterExerciser a ^long b] (long 1))) + (is (= (.methodOLL exerciser OLLadapter) "OLL")) + (def DDLadapter (fn [^double a ^double b] (long 1))) + (is (= (.methodDDL exerciser DDLadapter) "DDL")) + (def LDLadapter (fn [^long a ^double b] (long 1))) + (is (= (.methodLDL exerciser LDLadapter) "LDL")) + (def DLLadapter (fn [^double a ^long b] (long 1))) + (is (= (.methodDLL exerciser DLLadapter) "DLL")) + (def OOLadapter (fn [^AdapterExerciser a ^AdapterExerciser b] (long 1))) + (is (= (.methodOOL exerciser OOLadapter) "OOL")) + (def ODLadapter (fn [^AdapterExerciser a ^double b] (long 1))) + (is (= (.methodODL exerciser ODLadapter) "ODL")) + (def DOLadapter (fn [^double a ^AdapterExerciser b] (long 1))) + (is (= (.methodDOL exerciser DOLadapter) "DOL")) + (def LLIadapter (fn [^long a ^long b] 1)) + (is (= (.methodLLI exerciser LLIadapter) "LLI")) + (def LOIadapter (fn [^long a ^AdapterExerciser b] 1)) + (is (= (.methodLOI exerciser LOIadapter) "LOI")) + (def OLIadapter (fn [^AdapterExerciser a ^long b] 1)) + (is (= (.methodOLI exerciser OLIadapter) "OLI")) + (def DDIadapter (fn [^double a ^double b] 1)) + (is (= (.methodDDI exerciser DDIadapter) "DDI")) + (def LDIadapter (fn [^long a ^double b] 1)) + (is (= (.methodLDI exerciser LDIadapter) "LDI")) + (def DLIadapter (fn [^double a ^long b] 1)) + (is (= (.methodDLI exerciser DLIadapter) "DLI")) + (def OOIadapter (fn [^AdapterExerciser a ^AdapterExerciser b] 1)) + (is (= (.methodOOI exerciser OOIadapter) "OOI")) + (def ODIadapter (fn [^AdapterExerciser a ^double b] 1)) + (is (= (.methodODI exerciser ODIadapter) "ODI")) + (def DOIadapter (fn [^double a ^AdapterExerciser b] 1)) + (is (= (.methodDOI exerciser DOIadapter) "DOI")) + (def LLSadapter (fn [^long a ^long b] (short 1))) + (is (= (.methodLLS exerciser LLSadapter) "LLS")) + (def LOSadapter (fn [^long a ^AdapterExerciser b] (short 1))) + (is (= (.methodLOS exerciser LOSadapter) "LOS")) + (def OLSadapter (fn [^AdapterExerciser a ^long b] (short 1))) + (is (= (.methodOLS exerciser OLSadapter) "OLS")) + (def DDSadapter (fn [^double a ^double b] (short 1))) + (is (= (.methodDDS exerciser DDSadapter) "DDS")) + (def LDSadapter (fn [^long a ^double b] (short 1))) + (is (= (.methodLDS exerciser LDSadapter) "LDS")) + (def DLSadapter (fn [^double a ^long b] (short 1))) + (is (= (.methodDLS exerciser DLSadapter) "DLS")) + (def OOSadapter (fn [^AdapterExerciser a ^AdapterExerciser b] (short 1))) + (is (= (.methodOOS exerciser OOSadapter) "OOS")) + (def ODSadapter (fn [^AdapterExerciser a ^double b] (short 1))) + (is (= (.methodODS exerciser ODSadapter) "ODS")) + (def DOSadapter (fn [^double a ^AdapterExerciser b] (short 1))) + (is (= (.methodDOS exerciser DOSadapter) "DOS")) + (def LLBadapter (fn [^long a ^long b] (byte 1))) + (is (= (.methodLLB exerciser LLBadapter) "LLB")) + (def LOBadapter (fn [^long a ^AdapterExerciser b] (byte 1))) + (is (= (.methodLOB exerciser LOBadapter) "LOB")) + (def OLBadapter (fn [^AdapterExerciser a ^long b] (byte 1))) + (is (= (.methodOLB exerciser OLBadapter) "OLB")) + (def DDBadapter (fn [^double a ^double b] (byte 1))) + (is (= (.methodDDB exerciser DDBadapter) "DDB")) + (def LDBadapter (fn [^long a ^double b] (byte 1))) + (is (= (.methodLDB exerciser LDBadapter) "LDB")) + (def DLBadapter (fn [^double a ^long b] (byte 1))) + (is (= (.methodDLB exerciser DLBadapter) "DLB")) + (def OOBadapter (fn [^AdapterExerciser a ^AdapterExerciser b] (byte 1))) + (is (= (.methodOOB exerciser OOBadapter) "OOB")) + (def ODBadapter (fn [^AdapterExerciser a ^double b] (byte 1))) + (is (= (.methodODB exerciser ODBadapter) "ODB")) + (def DOBadapter (fn [^double a ^AdapterExerciser b] (byte 1))) + (is (= (.methodDOB exerciser DOBadapter) "DOB")) + (def LLDadapter (fn [^long a ^long b] (double 1))) + (is (= (.methodLLD exerciser LLDadapter) "LLD")) + (def LODadapter (fn [^long a ^AdapterExerciser b] (double 1))) + (is (= (.methodLOD exerciser LODadapter) "LOD")) + (def OLDadapter (fn [^AdapterExerciser a ^long b] (double 1))) + (is (= (.methodOLD exerciser OLDadapter) "OLD")) + (def DDDadapter (fn [^double a ^double b] (double 1))) + (is (= (.methodDDD exerciser DDDadapter) "DDD")) + (def LDDadapter (fn [^long a ^double b] (double 1))) + (is (= (.methodLDD exerciser LDDadapter) "LDD")) + (def DLDadapter (fn [^double a ^long b] (double 1))) + (is (= (.methodDLD exerciser DLDadapter) "DLD")) + (def OODadapter (fn [^AdapterExerciser a ^AdapterExerciser b] (double 1))) + (is (= (.methodOOD exerciser OODadapter) "OOD")) + (def ODDadapter (fn [^AdapterExerciser a ^double b] (double 1))) + (is (= (.methodODD exerciser ODDadapter) "ODD")) + (def DODadapter (fn [^double a ^AdapterExerciser b] (double 1))) + (is (= (.methodDOD exerciser DODadapter) "DOD")) + (def LLFadapter (fn [^long a ^long b] (float 1))) + (is (= (.methodLLF exerciser LLFadapter) "LLF")) + (def LOFadapter (fn [^long a ^AdapterExerciser b] (float 1))) + (is (= (.methodLOF exerciser LOFadapter) "LOF")) + (def OLFadapter (fn [^AdapterExerciser a ^long b] (float 1))) + (is (= (.methodOLF exerciser OLFadapter) "OLF")) + (def DDFadapter (fn [^double a ^double b] (float 1))) + (is (= (.methodDDF exerciser DDFadapter) "DDF")) + (def LDFadapter (fn [^long a ^double b] (float 1))) + (is (= (.methodLDF exerciser LDFadapter) "LDF")) + (def DLFadapter (fn [^double a ^long b] (float 1))) + (is (= (.methodDLF exerciser DLFadapter) "DLF")) + (def OOFadapter (fn [^AdapterExerciser a ^AdapterExerciser b] (float 1))) + (is (= (.methodOOF exerciser OOFadapter) "OOF")) + (def ODFadapter (fn [^AdapterExerciser a ^double b] (float 1))) + (is (= (.methodODF exerciser ODFadapter) "ODF")) + (def DOFadapter (fn [^double a ^AdapterExerciser b] (float 1))) + (is (= (.methodDOF exerciser DOFadapter) "DOF")) + (def LLOadapter (fn [^long a ^long b] exerciser)) + (is (= (.methodLLO exerciser LLOadapter) "LLO")) + (def LOOadapter (fn [^long a ^AdapterExerciser b] exerciser)) + (is (= (.methodLOO exerciser LOOadapter) "LOO")) + (def OLOadapter (fn [^AdapterExerciser a ^long b] exerciser)) + (is (= (.methodOLO exerciser OLOadapter) "OLO")) + (def DDOadapter (fn [^double a ^double b] exerciser)) + (is (= (.methodDDO exerciser DDOadapter) "DDO")) + (def LDOadapter (fn [^long a ^double b] exerciser)) + (is (= (.methodLDO exerciser LDOadapter) "LDO")) + (def DLOadapter (fn [^double a ^long b] exerciser)) + (is (= (.methodDLO exerciser DLOadapter) "DLO")) + (def OOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b] exerciser)) + (is (= (.methodOOO exerciser OOOadapter) "OOO")) + (def ODOadapter (fn [^AdapterExerciser a ^double b] exerciser)) + (is (= (.methodODO exerciser ODOadapter) "ODO")) + (def DOOadapter (fn [^double a ^AdapterExerciser b] exerciser)) + (is (= (.methodDOO exerciser DOOadapter) "DOO")) + (def OOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c] exerciser)) + (is (= (.methodOOOO exerciser OOOOadapter) "OOOO")) + (def OOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d] exerciser)) + (is (= (.methodOOOOO exerciser OOOOOadapter) "OOOOO")) + (def OOOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d ^AdapterExerciser e] exerciser)) + (is (= (.methodOOOOOO exerciser OOOOOOadapter) "OOOOOO")) + (def OOOOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d ^AdapterExerciser e ^AdapterExerciser f] exerciser)) + (is (= (.methodOOOOOOO exerciser OOOOOOOadapter) "OOOOOOO")) + (def OOOOOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d ^AdapterExerciser e ^AdapterExerciser f ^AdapterExerciser g] exerciser)) + (is (= (.methodOOOOOOOO exerciser OOOOOOOOadapter) "OOOOOOOO")) + (def OOOOOOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d ^AdapterExerciser e ^AdapterExerciser f ^AdapterExerciser g ^AdapterExerciser h] exerciser)) + (is (= (.methodOOOOOOOOO exerciser OOOOOOOOOadapter) "OOOOOOOOO")) + (def OOOOOOOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d ^AdapterExerciser e ^AdapterExerciser f ^AdapterExerciser g ^AdapterExerciser h ^AdapterExerciser i] exerciser)) + (is (= (.methodOOOOOOOOOO exerciser OOOOOOOOOOadapter) "OOOOOOOOOO")) + (def OOOOOOOOOOOadapter (fn [^AdapterExerciser a ^AdapterExerciser b ^AdapterExerciser c ^AdapterExerciser d ^AdapterExerciser e ^AdapterExerciser f ^AdapterExerciser g ^AdapterExerciser h ^AdapterExerciser i ^AdapterExerciser j] exerciser)) + (is (= (.methodOOOOOOOOOOO exerciser OOOOOOOOOOOadapter) "OOOOOOOOOOO"))) \ No newline at end of file diff --git a/test/clojure/test_clojure/java/io.clj b/test/clojure/test_clojure/java/io.clj index b83c63d311..78864220d6 100644 --- a/test/clojure/test_clojure/java/io.clj +++ b/test/clojure/test_clojure/java/io.clj @@ -13,6 +13,7 @@ FileInputStream InputStreamReader InputStream FileOutputStream OutputStreamWriter OutputStream ByteArrayInputStream ByteArrayOutputStream) + (clojure.lang RT) (java.net URL URI Socket ServerSocket))) (defn temp-file @@ -48,7 +49,7 @@ (slurp read-from :encoding "UTF-8"))) f f (.getAbsolutePath f) (.getAbsolutePath f) - (.toURL f) (.toURL f) + (RT/toUrl f) (RT/toUrl f) (.toURI f) (.toURI f) (FileOutputStream. f) (FileInputStream. f) (OutputStreamWriter. (FileOutputStream. f) "UTF-8") (reader f :encoding "UTF-8") @@ -60,6 +61,9 @@ (finally (.delete f))))) +(deftest clj-2783-slurp-maintains-backward-compatibility-errors + (is (thrown? java.io.FileNotFoundException (slurp "whitespace = #'\\s+'")))) + (deftest test-streams-nil (is (thrown-with-msg? IllegalArgumentException #"Cannot open.*nil" (reader nil))) (is (thrown-with-msg? IllegalArgumentException #"Cannot open.*nil" (writer nil))) diff --git a/test/clojure/test_clojure/java/javadoc.clj b/test/clojure/test_clojure/java/javadoc.clj index 575314cfa6..4ef665121f 100644 --- a/test/clojure/test_clojure/java/javadoc.clj +++ b/test/clojure/test_clojure/java/javadoc.clj @@ -9,14 +9,19 @@ (ns clojure.test-clojure.java.javadoc (:use clojure.test [clojure.java.javadoc :as j]) + (:require [clojure.string :as str]) (:import (java.io File))) +(defn correct-url [url-pattern-str module-name url-suffix] + (str (format url-pattern-str module-name) url-suffix)) + (deftest javadoc-url-test (testing "for a core api" (binding [*feeling-lucky* false] (are [x y] (= x (#'j/javadoc-url y)) nil "foo.Bar" - (str *core-java-api* "java/lang/String.html") "java.lang.String"))) + (correct-url *core-java-api* "java.base" "java/lang/String.html") "java.lang.String" + (correct-url *core-java-api* "java.sql" "java/sql/Connection.html") "java.sql.Connection"))) (testing "for a remote javadoc" (binding [*remote-javadocs* (ref (sorted-map "java." "http://example.com/"))] (is (= "http://example.com/java/lang/Number.html" (#'j/javadoc-url "java.lang.Number")))))) diff --git a/test/clojure/test_clojure/java/process.clj b/test/clojure/test_clojure/java/process.clj new file mode 100644 index 0000000000..3e187d58ec --- /dev/null +++ b/test/clojure/test_clojure/java/process.clj @@ -0,0 +1,28 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns clojure.test-clojure.java.process + (:require + [clojure.test :refer :all] + [clojure.java.process :as p] + [clojure.string :as str])) + +(deftest test-stderr-redirect + ;; capture to stdout and return string + (is (not (str/blank? (p/exec "bash" "-c" "ls")))) + + ;; print to stderr, capture nil + (is (str/blank? (p/exec "bash" "-c" "ls >&2"))) + + ;; redirect, then capture to string + (is (not (str/blank? (p/exec {:err :stdout} "bash" "-c" "ls >&2"))))) + +(deftest test-process-deref + (is (zero? @(p/exit-ref (p/start "sleep" "1")))) + (is (zero? (deref (p/exit-ref (p/start "sleep" "1")) 2500 :timeout))) + (is (= :timeout (deref (p/exit-ref (p/start "sleep" "1")) 1 :timeout)))) diff --git a/test/clojure/test_clojure/java_interop.clj b/test/clojure/test_clojure/java_interop.clj index 30fab56e56..3b28cafadc 100644 --- a/test/clojure/test_clojure/java_interop.clj +++ b/test/clojure/test_clojure/java_interop.clj @@ -11,9 +11,18 @@ (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.string :as str] + [clojure.test-clojure.proxy.examples :as proxy-examples] + [clojure.test-helper :refer [should-not-reflect]]) + (:import java.util.Base64 + (java.io File FileFilter FilenameFilter) + (java.util UUID) + (java.util.concurrent.atomic AtomicLong AtomicInteger) + (clojure.test FIConstructor FIStatic FunctionalTester AdapterExerciser))) ; http://clojure.org/java_interop ; http://clojure.org/compilation @@ -175,6 +184,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 @@ -373,7 +413,7 @@ (to-array [1 2 3]) )) (defn queue [& contents] - (apply conj (clojure.lang.PersistentQueue/EMPTY) contents)) + (apply conj clojure.lang.PersistentQueue/EMPTY contents)) (defn array-typed-equals [expected actual] (and (= (class expected) (class actual)) @@ -589,3 +629,263 @@ (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))))) + +(deftest array-type-symbols + (is (= long/1 (class (make-array Long/TYPE 0)))) + (is (= int/1 (class (make-array Integer/TYPE 0)))) + (is (= double/1 (class (make-array Double/TYPE 0)))) + (is (= short/1 (class (make-array Short/TYPE 0)))) + (is (= boolean/1 (class (make-array Boolean/TYPE 0)))) + (is (= byte/1 (class (make-array Byte/TYPE 0)))) + (is (= float/1 (class (make-array Float/TYPE 0)))) + (is (= String/1 (class (make-array String 0)))) + (is (= java.lang.String/1 (class (make-array String 0)))) + (is (= java.util.UUID/1 (class (make-array java.util.UUID 0)))) + (is (= `byte/1 'byte/1)) + (is (= `byte/3 'byte/3)) + (is (= `java.util.UUID/1 'java.util.UUID/1)) + (is (= `String/1 'java.lang.String/1)) + (is (= `java.lang.String/1 'java.lang.String/1)) + (is (= ['long/2] `[~'long/2]))) + + +(defn make-test-files [] + (let [id (str (UUID/randomUUID)) + temp-1 (java.io.File/createTempFile (str "test-1-" id)".edn") + temp-2 (java.io.File/createTempFile "test-2"".xml") + temp-3 (java.io.File/createTempFile (str "test-3-" id)".edn") + dir (File. (.getParent temp-3))] + {:dir dir :file-id id})) + +(defn return-long ^long [] + (let [^java.util.function.ToLongFunction f (fn ^long [x] 1)] + (Long/highestOneBit (.applyAsLong f :x)))) + +(deftest clojure-fn-as-java-fn + ;; pass Clojure fn as Java Predicate + (let [coll (java.util.ArrayList. [1 2 3 4 5])] + (is (true? (.removeIf coll even?))) + (is (= coll [1 3 5]))) + + ;; binding type hint triggers coercion + (is (instance? FileFilter + (let [^FileFilter ff (fn [f] true)] ff))) + + ;; coercion in let - reflection has types that should work + (let [{:keys [dir file-id]} (make-test-files) + ^FileFilter ff (fn [^File f] + (str/includes? (.getName f) file-id)) + filtered (.listFiles dir ff)] + (is (= 2 (count filtered)))) + + ;; coercion in let + (let [{:keys [dir file-id]} (make-test-files) + ^FileFilter ff (fn [^File f] + (str/includes? (.getName f) file-id)) + filtered (.listFiles ^File dir ff)] + (is (= 2 (count filtered)))) + + ;;; resolve method ambiguity using member symbol and param-tags + (let [{:keys [dir file-id]} (make-test-files) + ^FileFilter ff (fn [^File f] + (str/includes? (.getName f) file-id)) + filtered (^[FileFilter] File/.listFiles dir ff)] + (is (= 2 (count filtered)))) + + (defn files-with-ext [^File dir ext] + (vec (.list dir ^FilenameFilter #(str/ends-with? % ext)))) + + (let [{:keys [dir file-id]} (make-test-files) + ^FilenameFilter ff (fn [dir file-name] + (str/includes? file-name file-id)) + filtered (.list ^File dir ff)] + (is (= 2 (count filtered)))) + + (let [^java.util.function.DoubleToLongFunction f (fn [d] (int d))] + (is (instance? java.util.function.DoubleToLongFunction f)) + (is (= 10 (.applyAsLong f (double 10.6))))) + + (let [^java.util.function.IntConsumer f (fn [i] nil)] + (is (nil? (.accept f 42)))) + + (let [^java.util.function.IntPredicate f (fn [i] true)] + (is (true? (.test f 42)))) + + (let [arr (java.util.ArrayList. [1 2 3 4 5]) + ^java.util.function.ObjDoubleConsumer f (fn [arr i] nil)] + (is (nil? (.accept f arr 42)))) + + (let [f (constantly 100) + ^Runnable g f] + (is (identical? f g) "has been unintentionally adapted")) + + (let [^java.util.function.Predicate pred even? + coll1 (java.util.ArrayList. [1 2 3 4 5]) + coll2 (java.util.ArrayList. [6 7 8 9 10])] + (is (instance? java.util.function.Predicate pred)) + (is (true? (.removeIf coll1 pred))) + (is (= coll1 [1 3 5])) + (is (true? (.removeIf coll2 pred))) + (is (= coll2 [7 9]))) + + (let [^java.util.function.Predicate pred even? + coll1 (java.util.ArrayList. [1 2 3 4 5]) + cup-fn (java.util.ArrayList. [1 2 3 4 5])] + (is (instance? java.util.function.Predicate pred)) + (is (true? (.removeIf coll1 pred))) + (is (= coll1 [1 3 5])) + (is (true? (.removeIf cup-fn pred))) + (is (= cup-fn [1 3 5]))) + + (should-not-reflect #(clojure.test-clojure.java-interop/return-long)) + + ;; FI in class constructor + (let [^java.util.function.Predicate hinted-pred (fn [i] (> i 0)) + clj-pred (fn [i] (> i 0)) + fi-constructor-1 (FIConstructor. hinted-pred) + fi-constructor-2 (FIConstructor. clj-pred) + fi-constructor-3 (FIConstructor. (fn [i] (> i 0)))] + (is (= [1 2] (.numbers fi-constructor-1))) + (is (= [1 2] (.numbers fi-constructor-2))) + (is (= [1 2] (.numbers fi-constructor-3)))) + + ;; FI as arg to static + (let [^java.util.function.Predicate hinted-pred (fn [i] (> i 0)) + res (FIStatic/numbers hinted-pred)] + (is (= [1 2] res)))) + +(deftest eval-in-place-supplier-instance + (def stream (java.util.stream.Stream/generate ^java.util.function.Supplier (atom 42))) + (is (instance? java.util.stream.Stream stream))) + +(deftest eval-in-place-as-java-fn + (def filtered-list (.removeIf (java.util.ArrayList. [1 2 3 4 5]) even?)) + (is (true? filtered-list)) + + (def fi-constructor-numbers (.numbers (FIConstructor. (fn [i] (> i 0))))) + (is (= [1 2] fi-constructor-numbers)) + + (def fi-static (FIStatic/numbers (fn [i] (< i 0)))) + (is (= [-2 -1] fi-static))) + +;; newDirectoryStream is overloaded, takes ^[Path String] or ^[Path DirectoryStream$Filter] +;; so this method will reflect +(defn get-dir-stream [^java.nio.file.Path dir-path glob-pattern] + (let [path (.toPath (java.io.File. dir-path))] + (java.nio.file.Files/newDirectoryStream path glob-pattern))) + +(deftest test-reflection-to-overloaded-method-taking-FI + ;; all of these should resolve at runtime in reflection + (is (not (nil? (get-dir-stream "." "*")))) + (is (not (nil? (get-dir-stream "." (reify java.nio.file.DirectoryStream$Filter (accept [_ path] (.isDirectory (.toFile path)))))))) + ;; this one gets FI converted from IFn to DirectoryStream$Filter + (is (not (nil? (get-dir-stream "." (fn [^java.nio.file.Path path] (.isDirectory (.toFile path)))))))) + +;; we only support FI invoke coercion up to 10 args, this has 11 +(definterface ^{java.lang.FunctionalInterface true} FIWontWork + (invoke [a b c d e f g h i j k])) + +(definterface ReceivesFI + (call [^clojure.test_clojure.java_interop.FIWontWork fi])) + +(deftest test-reify-to-FI-allowed + ;; throws because there is no 11-arity invoker method and thus it is not possible to coerce + (is (thrown? ClassCastException + (eval '(let [^clojure.test_clojure.java_interop.FIWontWork f + (fn [p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 p11] p11)] + (.invoke f 1 2 3 4 5 6 7 8 9 10 11))))) + + (let [r (reify clojure.test_clojure.java_interop.ReceivesFI + (call [_ fi] (.invoke fi 0 0 0 0 0 0 0 0 0 0 1)))] + + ;; doesn't throw at compilation time, but throws at runtime + ;; because IFn cannot be implicitly converted + (is (thrown? ClassCastException + (.call r (fn [a b c d e f g h i j k] k)))) + + ;; works because the reify implements the FI, no conversion necessary + (is (= 1 (.call r (reify clojure.test_clojure.java_interop.FIWontWork (invoke [_ a b c d e f g h i j k] k))))))) + +(definterface ^{java.lang.FunctionalInterface true} FIPrims + (^long invoke [^long a ^long b ^long c ^long d])) + +(definterface ReceivesFIPrims + (call [^clojure.test_clojure.java_interop.FIPrims fi])) + +(deftest test-match-prim-args-only-to-2 + (let [r (reify clojure.test_clojure.java_interop.ReceivesFIPrims + (call [_ fi] (.invoke fi 1 2 3 4)))] + (is (= 4 (.call r (fn [^long a ^long b ^long c ^long d] d)))))) + +(deftest test-invoke-fiprim-rets + (let [^clojure.test_clojure.java_interop.FIPrims f (fn [a b c d] a)] + (is (instance? clojure.test_clojure.java_interop.FIPrims f)) + (is (= 1 (.invoke f 1 2 3 4)))) + + (is (= "LLL" (AdapterExerciser/.methodLLL (AdapterExerciser.) (fn ^long [^long a ^long b])))) + (is (= "OOOO" (AdapterExerciser/.methodOOOO (AdapterExerciser.) (fn ^long [^long a ^long b ^long c])))) + ) + +(deftest test-null-reify + (is (= "null" ((fn [x] (FIStatic/allowsNullFI x)) nil)))) + +(deftest test-FI-subtype + (is (= [1 2 3 4 5] (->> (java.util.stream.Stream/iterate 1 inc) stream-seq! (take 5))))) + +(deftest class-methods-with-fi-args + (testing "Constructor accepting FI arg, provided overloaded static class FI" + (let [fi (FunctionalTester. "Constructor" 0 FunctionalTester/getChar)] + (is (= \C (.testVar fi))))) + + (testing "Instance method accepting FI arg, provided overloaded static class FI" + (let [fi (FunctionalTester. "asf" 0 FunctionalTester/getChar)] + (.instanceMethodWithFIArg fi "Instance" 0 FunctionalTester/getChar) + (is (= \I (.testVar fi))))) + + (testing "Static method accepting FI arg, provided overloaded static class FI" + (is (= \S (FunctionalTester/staticMethodWithFIArg "Static" 0 FunctionalTester/getChar))))) + +;; call is reflective and one overload takes an FI (Supplier) +(definterface TakesFIOverloaded + (call [^java.util.function.Supplier s]) + (call [^String s])) + +(deftest CLJ-2898-reified-objs-both-IFn-and-FI + ;; f is both IFn and FI (Supplier) + (let [f (reify + java.util.function.Supplier + (get [_] 100) + + clojure.lang.IFn + (applyTo [_ _] 201) + (invoke [_] 200))] + + ;; should not be adapted. use Supplier.get() impl on tl + (is (= 100 (.get (ThreadLocal/withInitial f)))) + + (let [tfio (reify TakesFIOverloaded + (call [_ ^java.util.function.Supplier o] (.get o)) + (call [_ ^String s] "string"))] + ;; reflective call to TakesFIOverloaded.call() + ;; as above, should not be adapted and use Supplier.get() + (is (= 100 (.call tfio (identity f))))))) + +(deftest CLJ-2914-Qualified-Method-Expr-NPE + (is (fails-with-cause? IllegalArgumentException + #"Malformed method expression.*String/\.length" + (eval '(fn [] (java.lang.String/.length)))))) diff --git a/test/clojure/test_clojure/keywords.clj b/test/clojure/test_clojure/keywords.clj index 9ef86d7dd2..b486a067ab 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 \(> 20\) passed to: :foo/bar" (apply :foo/bar (range 21)))) + (is (thrown-with-msg? IllegalArgumentException #"Wrong number of args \(> 20\) passed to: :foo/bar" (apply :foo/bar (range 22))))) diff --git a/test/clojure/test_clojure/main.clj b/test/clojure/test_clojure/main.clj index 31bec96659..5909672c8c 100644 --- a/test/clojure/test_clojure/main.clj +++ b/test/clojure/test_clojure/main.clj @@ -56,7 +56,7 @@ (.setStackTrace (into-array java.lang.StackTraceElement nil))) tr-data (-> e Throwable->map main/ex-triage)] (is (= tr-data #:clojure.error{:phase :execution, :class 'java.lang.Error, :cause "xyz"})) - (is (= (main/ex-str tr-data) "Execution error (Error) at (REPL:1).\nxyz\n")))) + (is (= (main/ex-str tr-data) (platform-newlines "Execution error (Error) at (REPL:1).\nxyz\n"))))) (defn s->lpr [s] 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/metadata.clj b/test/clojure/test_clojure/metadata.clj index d0158ab2a7..e0e4fe58e6 100644 --- a/test/clojure/test_clojure/metadata.clj +++ b/test/clojure/test_clojure/metadata.clj @@ -51,6 +51,13 @@ (def ^{:a 1} foo 0) #'foo)] (is (= 1 (-> v meta :a))))) + (testing "const vars preserve metadata" + (let [[v1 v2] (eval-in-temp-ns + (def ^:const foo ^:foo []) + (def ^:const bar ^:foo [:bar]) + [(meta foo) (meta bar)])] + (is (= {:foo true} v1)) + (is (= {:foo true} v2)))) #_(testing "subsequent declare doesn't overwrite metadata" (let [v (eval-in-temp-ns (def ^{:b 2} bar 0) diff --git a/test/clojure/test_clojure/method_thunks.clj b/test/clojure/test_clojure/method_thunks.clj new file mode 100644 index 0000000000..b999e539a4 --- /dev/null +++ b/test/clojure/test_clojure/method_thunks.clj @@ -0,0 +1,62 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. +; Authors: Fogus + +(ns clojure.test-clojure.method-thunks + (:use clojure.test) + (:require [clojure.java.io :as jio]) + (:import (clojure.lang Compiler Tuple) + (java.util Arrays UUID Locale) + (java.io File FileFilter) + clojure.lang.IFn$LL)) + +(set! *warn-on-reflection* true) + +(deftest method-arity-selection + (is (= '([] [] []) + (take 3 (repeatedly ^[] Tuple/create)))) + (is (= '([1] [2] [3]) + (map ^[_] Tuple/create [1 2 3]))) + (is (= '([1 4] [2 5] [3 6]) + (map ^[_ _] Tuple/create [1 2 3] [4 5 6])))) + +(deftest method-signature-selection + (is (= [1.23 3.14] + (map ^[double] Math/abs [1.23 -3.14]))) + (is (= [(float 1.23) (float 3.14)] + (map ^[float] Math/abs [1.23 -3.14]))) + (is (= [1 2 3] + (map ^[long] Math/abs [1 2 -3]))) + (is (= [#uuid "00000000-0000-0001-0000-000000000002"] + (map ^[long long] UUID/new [1] [2]))) + (is (= '("a" "12") + (map ^[Object] String/valueOf ["a" 12]))) + (is (= ["A" "B" "C"] + (map ^[java.util.Locale] String/.toUpperCase ["a" "b" "c"] (repeat java.util.Locale/ENGLISH)))) + (is (thrown? ClassCastException + (doall (map ^[long] String/valueOf [12 "a"])))) + (testing "bad method names" + (is (thrown-with-msg? Exception #"static method" (eval 'java.lang.String/foo))) + (is (thrown-with-msg? Exception #"instance method" (eval 'java.lang.String/.foo))) + (is (thrown-with-msg? Exception #"constructor" (eval 'Math/new))))) + +(def mt ^[_] Tuple/create) +(def mts {:fromString ^[_] UUID/fromString}) +(def gbs ^[] String/.getBytes) + +(deftest method-thunks-in-structs + (is (= #uuid "00000000-0000-0001-0000-000000000002" + ((:fromString mts) "00000000-0000-0001-0000-000000000002"))) + (is (= [1] (mt 1))) + (is (= 97 (first (seq (gbs "a")))))) + +(deftest hinted-method-values + (is (thrown? Exception (eval '(.listFiles (jio/file ".") #(File/.isDirectory %))))) + (is (seq (.listFiles (jio/file ".") ^FileFilter File/.isDirectory))) + (is (seq (File/.listFiles (jio/file ".") ^FileFilter File/.isDirectory))) + (is (seq (^[FileFilter] File/.listFiles (jio/file ".") ^FileFilter File/.isDirectory)))) 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..be4f7a0a60 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) @@ -595,7 +598,17 @@ Math/pow overflows to Infinity." (is (== (denominator 1/2) 2)) (is (== (numerator 1/2) 1)) (is (= (bigint (/ 100000000000000000000 3)) 33333333333333333333)) - (is (= (long 10000000000000000000/3) 3333333333333333333))) + (is (= (long 10000000000000000000/3) 3333333333333333333)) + + ;; special cases around Long/MIN_VALUE + (is (= (/ 1 Long/MIN_VALUE) -1/9223372036854775808)) + (is (true? (< (/ 1 Long/MIN_VALUE) 0))) + (is (true? (< (* 1 (/ 1 Long/MIN_VALUE)) 0))) + (is (= (abs (/ 1 Long/MIN_VALUE)) 1/9223372036854775808)) + (is (false? (< (abs (/ 1 Long/MIN_VALUE)) 0))) + (is (false? (< (* 1 (abs (/ 1 Long/MIN_VALUE))) 0))) + (is (= (/ Long/MIN_VALUE -3) 9223372036854775808/3)) + (is (false? (< (/ Long/MIN_VALUE -3) 0)))) (deftest test-arbitrary-precision-subtract (are [x y] (= x y) @@ -644,6 +657,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 958df5eca8..b7e3637925 100644 --- a/test/clojure/test_clojure/other_functions.clj +++ b/test/clojure/test_clojure/other_functions.clj @@ -102,6 +102,10 @@ (deftest test-constantly (let [c0 (constantly 10)] (are [x] (= 10 (c0 x)) + nil + 42 + "foo") + (are [x] (= 10 (c0 x :a :b :c)) nil 42 "foo"))) @@ -277,7 +281,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 @@ -348,6 +354,19 @@ ; Regex Support ; re-matcher re-find re-matches re-groups re-seq +(deftest test-regex-matcher + (let [matcher (re-matcher #"(\d{2})/(\d{2})/(\d{4})" "12/02/1975")] + (is (= ["12/02/1975" "12" "02" "1975"] (re-find matcher))) + (is (= ["12/02/1975" "12" "02" "1975"] (re-groups matcher))) + (is (= "12/02/1975" (nth matcher 0) (nth matcher 0 :foo))) + (is (= "12" (nth matcher 1) (nth matcher 1 :foo))) + (is (= "02" (nth matcher 2) (nth matcher 2 :foo))) + (is (= "1975" (nth matcher 3) (nth matcher 3 :foo))) + (is (thrown? IndexOutOfBoundsException (nth matcher -1))) + (is (= :foo (nth matcher -1 :foo))) + (is (thrown? IndexOutOfBoundsException (nth matcher 4))) + (is (= :foo (nth matcher 4 :foo))))) + ; update (deftest test-update @@ -366,3 +385,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/param_tags.clj b/test/clojure/test_clojure/param_tags.clj new file mode 100644 index 0000000000..bccd3ef127 --- /dev/null +++ b/test/clojure/test_clojure/param_tags.clj @@ -0,0 +1,220 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. +; Authors: Fogus + +(ns clojure.test-clojure.param-tags + (:use clojure.test) + (:require + [clojure.string :as str] + [clojure.reflect :as r] + [clojure.test-helper :refer [should-not-reflect]]) + (:import + (clojure.test SwissArmy ConcreteClass) + (clojure.lang Tuple Compiler Compiler$CompilerException) + (java.util Arrays UUID Locale))) + +(set! *warn-on-reflection* true) + +(deftest no-hints-with-param-tags + (should-not-reflect + (defn touc-no-reflect [s] + (^[] String/.toUpperCase s))) + (should-not-reflect + (defn touc-no-reflectq [s] + (^[] java.lang.String/.toUpperCase s))) + (should-not-reflect + (defn touc-no-reflect-arg-tags [s] + (^[java.util.Locale] String/.toUpperCase s java.util.Locale/ENGLISH))) + (should-not-reflect + (defn no-overloads-no-reflect [v] + (java.time.OffsetDateTime/.getYear v)))) + +(deftest no-param-tags-use-qualifier + ;; both Date and OffsetDateTime have .getYear - want to show here the qualifier is used + (let [f (fn [^java.util.Date d] (java.time.OffsetDateTime/.getYear d)) + date (java.util.Date. 1714495523100)] + ;; works when passed OffsetDateTime + (is (= 2024 (f (-> date .toInstant (.atOffset java.time.ZoneOffset/UTC))))) + + ;; fails when passed Date, expects OffsetDateTime + (is (thrown? ClassCastException + (f date))))) + +(deftest param-tags-in-invocation-positions + (testing "qualified static method invocation" + (is (= 3 (^[long] Math/abs -3))) + (is (= [1 2] (^[_ _] Tuple/create 1 2))) + (is (= "42" (Long/toString 42)))) + (testing "qualified ctor invocation" + (is (= (^[long long] UUID/new 1 2) #uuid "00000000-0000-0001-0000-000000000002")) + (is (= (^[long long] java.util.UUID/new 1 2) #uuid "00000000-0000-0001-0000-000000000002")) + (is (= "a" (^[String] String/new "a")))) + (testing "qualified instance method invocation" + (is (= \A (String/.charAt "A" 0))) + (is (= "A" (^[java.util.Locale] String/.toUpperCase "a" java.util.Locale/ENGLISH))) + (is (= "A" (^[Locale] String/.toUpperCase "a" java.util.Locale/ENGLISH))) + (is (= 65 (aget (^[String] String/.getBytes "A" "US-ASCII") 0))) + (is (= "42" (^[] Long/.toString 42)))) + (testing "string repr array type resolutions" + (let [lary (long-array [1 2 3 4 99 100]) + oary (into-array [1 2 3 4 99 100]) + sary (into-array String ["a" "b" "c"])] + (is (= 4 (^[longs long] Arrays/binarySearch lary (long 99)))) + (is (= 4 (^[objects _] Arrays/binarySearch oary 99))) + (is (= 4 (^["[Ljava.lang.Object;" _] Arrays/binarySearch oary 99))) + (is (= 1 (^["[Ljava.lang.Object;" _] Arrays/binarySearch sary "b"))))) + (testing "bad method names" + (is (thrown? Exception (eval '(^[] java.lang.String/foo "a")))) + (is (thrown? Exception (eval '(^[] java.lang.String/.foo "a")))) + (is (thrown? Exception (eval '(^[] Math/new "a")))))) + + +;; Mapping of symbols returned from reflect call to :parameter-type used as arguments to .getDeclaredMethod, +;; :arg-type used as arguments to the methods and constructors being tested, :arg-tag used as arg-tags +;; to the methods and constructors being tested. +(def reflected-parameter-types {'int {:parameter-type Integer/TYPE + :arg-type "(int 42)" + :arg-tag "int"} + 'boolean {:parameter-type Boolean/TYPE + :arg-type "true" + :arg-tag "boolean"} + 'long {:parameter-type Long/TYPE + :arg-type "42" + :arg-tag "long"} + 'long<> {:parameter-type (Class/forName "[J") + :arg-type "(long-array [1 2])" + :arg-tag "long*"} + 'int<><> {:parameter-type (Class/forName "[[I") + :arg-type "(make-array Integer/TYPE 1 2)" + :arg-tag "int**"} + 'java.lang.Object<> {:parameter-type (Class/forName "[Ljava.lang.Object;") + :arg-type "(into-array [1 2])" + :arg-tag "\"[Ljava.lang.Object;\""} + 'java.lang.String<> {:parameter-type (Class/forName "[Ljava.lang.String;") + :arg-type "(into-array [\"a\" \"b\"])" + :arg-tag "\"[Ljava.lang.String;\""}}) + +(defn is-static-method? [class method-name params] + (let [method (.getDeclaredMethod ^Class class ^String (name method-name) ^"[Ljava.lang.Object;" params)] + (java.lang.reflect.Modifier/isStatic (.getModifiers method)))) + +(defn get-methods + "Reflect the class located at `path`, filter out the public members, add a :type + of :constructor, :static, or :instance to each." + [path] + (let [reflected-class (r/reflect (resolve path)) + public (filter #(contains? (:flags %) :public) (:members reflected-class))] + (reduce (fn [res m] + (let [class (-> m :declaring-class resolve) + params (into-array Class (map #(-> % reflected-parameter-types :parameter-type) (:parameter-types m)))] + (cond + (not (contains? m :return-type)) (conj res (assoc m :type :constructor)) + (is-static-method? class (:name m) params) (conj res (assoc m :type :static)) + :else (conj res (assoc m :type :instance))))) + [] public))) + +(defn exercise-constructor + "Provided a map of data returned from a call to reflect representing a constructor. + Construct a new instance of the class providing the appropriate arg-tags and return + a map containing the new instance and expected target class" + [{:keys [declaring-class parameter-types] :as m}] + (let [target-class (-> declaring-class str Class/forName) + args (str/join " " (map #(-> % reflected-parameter-types :arg-type) parameter-types)) + arg-tags (str/join " " (map #(-> % reflected-parameter-types :arg-tag) parameter-types)) + fun-call-str (read-string (str "(^[" arg-tags "] " declaring-class ". " args ")")) + _ (should-not-reflect #(eval 'fun-call-str)) + new-instance (eval fun-call-str)] + {:expected target-class :actual new-instance})) + +(defn exercise-static-method + "Provided a map of data returned from a call to reflect representing a static class method. + Call the static method providing the appropriate arg-tags and return a map containing + the actual and expected response." + [{:keys [name declaring-class parameter-types]}] + (let [class (str declaring-class) + method (str name) + args (str/join " " (map #(-> % reflected-parameter-types :arg-type) parameter-types)) + arg-tags (str/join " " (map #(-> % reflected-parameter-types :arg-tag) parameter-types)) + expected-response (str/join "-" parameter-types) + fun-call-str (read-string (str "(^[" arg-tags "] " class "/" method " " args ")")) + _ (should-not-reflect #(eval 'fun-call-str)) + response (eval fun-call-str)] + {:expected expected-response :actual response})) + +(defn exercise-instance-method + "Provided a map of data returned from a call to reflect representing a class instance method. + Call the method providing the appropriate arg-tags and return a map containing + the actual and expected response." + [{:keys [name declaring-class parameter-types]}] + (let [method (str "." name) + args (str/join " " (map #(-> % reflected-parameter-types :arg-type) parameter-types)) + arg-tags (str/join " " (map #(-> % reflected-parameter-types :arg-tag) parameter-types)) + expected-response (str/join "-" parameter-types) + fun-call-str (read-string (str "(^[" arg-tags "] " declaring-class "/" method " " "(" declaring-class ".)" " " args ")")) + _ (should-not-reflect #(eval 'fun-call-str)) + response (eval fun-call-str)] + {:expected expected-response :actual response})) + +(deftest arg-tags-in-constructors-and-static-and-instance-methods + (doseq [m (get-methods 'clojure.test.SwissArmy)] + (case (:type m) + :constructor (let [{:keys [expected actual]} (exercise-constructor m)] + (is (instance? expected actual))) + :static (let [{:keys [expected actual]} (exercise-static-method m)] + (is (= expected actual))) + :instance (let [{:keys [expected actual]} (exercise-instance-method m)] + (is (= expected actual)))))) + +(deftest field-overloads-method-CLJ-2899-regression + (testing "overloaded in value position" + (is (= "static-field" clojure.test.SwissArmy/doppelganger))) + + (testing "overloaded in value position, w/paramtags" + (is (= "" (apply ^[] clojure.test.SwissArmy/doppelganger [])))) + + (testing "overloaded, invoke no args" + (is (= "" (clojure.test.SwissArmy/doppelganger)))) + + (testing "overloaded, invoke w/args" + (is (= "int-int-long" (clojure.test.SwissArmy/doppelganger (int 1) (int 2) (long 42))))) + + (testing "non-overloaded, field holds IFn, invoke w/args fails" + (is (thrown? Exception (eval '(clojure.test.SwissArmy/idFn 42)))) + (is (= #'clojure.core/identity clojure.test.SwissArmy/idFn))) + + (testing "non-overloaded, field holds IFn, invoke no args" + (is (= #'clojure.core/identity (clojure.test.SwissArmy/idFn)))) + + (testing "instance method overloads" + (is (= "int-int" (clojure.test.SwissArmy/.doppelganger (clojure.test.SwissArmy/new) (int 1) (int 2)))) + (is (= "int-int" (apply clojure.test.SwissArmy/.doppelganger (clojure.test.SwissArmy/new) (int 1) (int 2) []))))) + +(defmacro arg-tags-called-in-macro + [a-type b-type a b] + `(^[~a-type ~b-type] SwissArmy/staticArityOverloadMethod ~a ~b)) + +(deftest arg-tags-in-macro + (is (= "int-int" (arg-tags-called-in-macro int int 1 2)))) + +(deftest bridge-methods + (testing "Allows correct intended usage." + (let [concrete (ConcreteClass.)] + (is (= 42 (^[Integer] ConcreteClass/.stampWidgets concrete (int 99)))))) + (testing "Will not call bridge method." + (is (thrown? Compiler$CompilerException + (eval '(let [concrete (clojure.test.ConcreteClass.)] + (^[Object] ConcreteClass/.stampWidgets concrete (int 99)))))))) + + +(deftest incorrect-arity-invocation-error-messages + + (testing "Invocation with param-tags having incorrect number of args" + (let [e (try + (eval '(^[long] Math/abs -1 -2 -3)) + (catch Compiler$CompilerException e (str "-> " (.getMessage (.getCause e)))))] + (is (not (nil? (re-find #"expected 1.*received 3" e))) "Error message was expected to indicate 1 argument was expected but 2 were provided")))) 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/pprint/test_pretty.clj b/test/clojure/test_clojure/pprint/test_pretty.clj index c9b321fdff..4d31a21396 100644 --- a/test/clojure/test_clojure/pprint/test_pretty.clj +++ b/test/clojure/test_clojure/pprint/test_pretty.clj @@ -384,3 +384,32 @@ It is implemented with a number of custom enlive templates.\" ["#inst \"2014-04-29T14:00:00.000+00:00\""]) "calendar object pretty prints"))) +(deftest test-print-meta + (let [r (with-meta (range 24) {:b 2})] + (are [expected val] (= (platform-newlines expected) (with-out-str (binding [*print-meta* true] (pprint val)))) + "^{:a 1, :b 2} {:x 1, :y 2}\n" + ^{:a 1 :b 2} {:x 1 :y 2} + + "^{:a 1, :b 2}\n{:x\n ^{:b 2}\n (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23),\n :y 1}\n" + ^{:a 1 :b 2} {:x r :y 1} + + "^{:a 1} {:x ^{:foo true} {:y 2}}\n" + ^{:a 1} {:x ^:foo {:y 2}} + + "^{:a 1} [1 2 3 4]\n" + ^{:a 1} [1 2 3 4] + + "^{:a 1} [^{:b 2} [1 2]]\n" + ^{:a 1} [^{:b 2} [1 2]] + + "^{:a 1}\n[[[1\n ^{:b 2}\n (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23)]]]\n" + ^{:a 1} [[[1 ^{:b 2} r]]] + + "^{:line 409, :column 16} (1 2 3 4)\n" + ^{:a 1} '(1 2 3 4) + + "^{:a 1} (0 1 2 3)\n" + ^{:a 1} (with-meta (range 4) {:a 1}) + + "^{:a 1} #{1 4 3 2}\n" + ^{:a 1} #{1 2 3 4}))) \ No newline at end of file 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/printer.clj b/test/clojure/test_clojure/printer.clj index 3d9cc65f8f..0216f4f48f 100644 --- a/test/clojure/test_clojure/printer.clj +++ b/test/clojure/test_clojure/printer.clj @@ -14,7 +14,9 @@ ;; Created 29 October 2008 (ns clojure.test-clojure.printer - (:use clojure.test)) + (:use clojure.test + [clojure.test-helper :only [platform-newlines]]) + (:require [clojure.pprint :refer [pprint]])) (deftest print-length-empty-seq (let [coll () val "()"] @@ -121,8 +123,9 @@ (deftest print-throwable (binding [*data-readers* {'error identity}] - (are [e] (= (-> e Throwable->map) - (-> e pr-str read-string)) + (are [e] (let [e' e] ;; use same templated exception object in both cases + (= (-> e' Throwable->map) + (-> e' pr-str read-string))) (Exception. "heyo") (Throwable. "I can a throwable" (Exception. "chain 1" @@ -135,8 +138,49 @@ {:with "even" :more 'data}))))))) (deftest print-ns-maps - (is (= "#:user{:a 1}" (binding [*print-namespace-maps* true] (pr-str {:user/a 1})))) - (is (= "{:user/a 1}" (binding [*print-namespace-maps* false] (pr-str {:user/a 1})))) + (are [m s-on pp-on s-off] + (and (= s-on (binding [*print-namespace-maps* true] (pr-str m))) + (= (platform-newlines pp-on) (binding [*print-namespace-maps* true] (with-out-str (pprint m)))) + (= s-off (binding [*print-namespace-maps* false] (pr-str m)))) + {} "{}" "{}\n" "{}" + {:a 1, :b 2} "{:a 1, :b 2}" "{:a 1, :b 2}\n" "{:a 1, :b 2}" + {:user/a 1} "#:user{:a 1}" "#:user{:a 1}\n" "{:user/a 1}" + {:user/a 1, :user/b 2} "#:user{:a 1, :b 2}" "#:user{:a 1, :b 2}\n" "{:user/a 1, :user/b 2}" + {:user/a 1, :b 2} "{:user/a 1, :b 2}" "{:user/a 1, :b 2}\n" "{:user/a 1, :b 2}" + {:user/a 1, 'user/b 2} "#:user{:a 1, b 2}" "#:user{:a 1, b 2}\n" "{:user/a 1, user/b 2}" + {:user/a 1, :foo/b 2} "{:user/a 1, :foo/b 2}" "{:user/a 1, :foo/b 2}\n" "{:user/a 1, :foo/b 2}" + + {:user/a 1, :user/b 2, 100 200} + "{:user/a 1, :user/b 2, 100 200}" + "{:user/a 1, :user/b 2, 100 200}\n" + "{:user/a 1, :user/b 2, 100 200}" + + ;; CLJ-2469 + (struct (create-struct :q/a :q/b :q/c) 1 2 3) + "#:q{:a 1, :b 2, :c 3}" + "#:q{:a 1, :b 2, :c 3}\n" + "{:q/a 1, :q/b 2, :q/c 3}" + + ;; CLJ-2537 + {:x.y/a {:rem 0}, :x.y/b {:rem 1}} + "#:x.y{:a {:rem 0}, :b {:rem 1}}" + "#:x.y{:a {:rem 0}, :b {:rem 1}}\n" + "{:x.y/a {:rem 0}, :x.y/b {:rem 1}}" + + (into (sorted-map-by (fn [k1 k2] + (when-not (every? qualified-ident? [k1 k2]) + (throw (RuntimeException. (str "Invalid keys:" [k1 k2])))) + (compare k1 k2)) + :x.y/a {:rem 0}, :x.y/b {:rem 1})) + "#:x.y{:a {:rem 0}, :b {:rem 1}}" + "#:x.y{:a {:rem 0}, :b {:rem 1}}\n" + "{:x.y/a {:rem 0}, :x.y/b {:rem 1}}" + + (sorted-map-by #(compare %2 %1) :k/a 1 :k/b 2 :k/c 3 :k/d 4 :k/e 5 :k/f 6 :k/g 7 :k/h 8 :k/i 9) + "#:k{:i 9, :h 8, :g 7, :f 6, :e 5, :d 4, :c 3, :b 2, :a 1}" + "#:k{:i 9, :h 8, :g 7, :f 6, :e 5, :d 4, :c 3, :b 2, :a 1}\n" + "{:k/i 9, :k/h 8, :k/g 7, :k/f 6, :k/e 5, :k/d 4, :k/c 3, :k/b 2, :k/a 1}") + (let [date-map (bean (java.util.Date. 0))] (is (= (binding [*print-namespace-maps* true] (pr-str date-map)) (binding [*print-namespace-maps* false] (pr-str date-map)))))) diff --git a/test/clojure/test_clojure/protocols.clj b/test/clojure/test_clojure/protocols.clj index 3072915481..4cd87a42b5 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,48 @@ (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")))) + +;; CLJ-2698 - Ignore primitive hints in defprotocol return + +(defprotocol PrimHinted + (^double f1 [p a]) + (^void f2 [p])) + +(deftest test-prim-ret-hints-ignored + (is (thrown-with-msg? + Exception + #"Receiver class" + (eval '(f1 (reify PrimHinted) :foo)))) + (is (= :foo (f1 (reify PrimHinted (f1 [_ x] x)) :foo))) + (is (= "foo" (f2 (reify PrimHinted (f2 [_] "foo")))))) 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/reader.cljc b/test/clojure/test_clojure/reader.cljc index 522a27928a..a0e1f1f13e 100644 --- a/test/clojure/test_clojure/reader.cljc +++ b/test/clojure/test_clojure/reader.cljc @@ -428,8 +428,9 @@ (doseq [form top-levels] (clojure.walk/postwalk #(when (list? %) - (is (= (expected-metadata (first %)) - (meta %))) + (when (contains? expected-metadata (first %)) + (is (= (expected-metadata (first %)) + (meta %)))) (is (->> (meta %) vals (filter number?) @@ -453,7 +454,26 @@ ;; Anonymous function literal (#()) -(deftest t-Anonymouns-function-literal) +(deftest t-Anonymous-function-literal + ;; #(vector %) => #(fn* [gen__#] (vector gen__#)) + ;; [\S]+ matches the anon arg, then \1 is backref matching first group + (is (= "(fn* [] (vector))" (pr-str (read-string "#(vector)")))) + (is (not (nil? (re-matches #"\(fn\* \[([\S]+)] \(vector \1\)\)" (pr-str (read-string "#(vector %)")))))) + (is (not (nil? (re-matches #"\(fn\* \[([\S]+)] \(vector \1 \1\)\)" (pr-str (read-string "#(vector % %)")))))) + (is (not (nil? (re-matches #"\(fn\* \[([\S]+)] \(vector \1 \1\)\)" (pr-str (read-string "#(vector % %1)")))))) + (is (not (nil? (re-matches #"\(fn\* \[([\S]+) ([\S]+)] \(vector \1 \2\)\)" (pr-str (read-string "#(vector %1 %2)")))))) + (is (not (nil? (re-matches #"\(fn\* \[([\S]+) ([\S]+) & ([\S]+)] \(vector \2 \3\)\)" (pr-str (read-string "#(vector %2 %&)")))))) + + ;; invalid formats + (is (thrown? RuntimeException (read-string "#(vector %%)"))) + (is (thrown? RuntimeException (read-string "#(vector %1/2)"))) + (is (thrown? RuntimeException (read-string "#(vector %1.5)"))) + (is (thrown? RuntimeException (read-string "#(vector %-0.2)"))) + (is (thrown? RuntimeException (read-string "#(vector %3M)"))) + + ;; check sneaking in discarded content + (is (thrown? RuntimeException (read-string "#(list %#_(first arg)1.00000001 %#_(secong arg)2r10 %#_(rest arg)-1.5)"))) + ) ;; Syntax-quote (`, note, the "backquote" character), Unquote (~) and ;; Unquote-splicing (~@) diff --git a/test/clojure/test_clojure/reflect.clj b/test/clojure/test_clojure/reflect.clj index 36ea68172e..2cb92a5a8f 100644 --- a/test/clojure/test_clojure/reflect.clj +++ b/test/clojure/test_clojure/reflect.clj @@ -51,4 +51,13 @@ (checkCLJ2066 (javax.xml.stream.XMLInputFactory/newInstance)) ;; CLJ-2414 - find default method on interface of inaccessible class - (checkCLJ2414 (java.nio.file.Paths/get "src" (into-array String [])))) \ No newline at end of file + (checkCLJ2414 (java.nio.file.Paths/get "src" (into-array String [])))) + +(defn sleep [ms] + ;; in Java <19, does not reflect. Java >= 19, does reflect + (Thread/sleep ms)) + + +(deftest check-CLJ-2843 + (sleep 1) + (sleep (Integer/valueOf 1))) diff --git a/test/clojure/test_clojure/repl/deps.clj b/test/clojure/test_clojure/repl/deps.clj new file mode 100644 index 0000000000..a2a69078d1 --- /dev/null +++ b/test/clojure/test_clojure/repl/deps.clj @@ -0,0 +1,30 @@ +(ns clojure.test-clojure.repl.deps + (:use clojure.test) + (:require [clojure.string :as str] + [clojure.repl.deps :as deps] + [clojure.main :as main])) + +(defmacro with-dynamic-loader + "Ensure or install a DynamicClassLoader as the current thread's + context classloader and execute the body." + [& body] + `(let [t# (Thread/currentThread) + cl# (.getContextClassLoader t#)] + (if (instance? ~'clojure.lang.DynamicClassLoader cl#) + (do ~@body) + (try + (.setContextClassLoader t# (clojure.lang.DynamicClassLoader. cl#)) + ~@body + (finally + (.setContextClassLoader t# cl#)))))) + +;(deftest test-no-add-libs-outside-repl +; (try +; (deps/add-lib 'org.clojure/data.json {:mvn/version "2.4.0"}) +; (is false "add-libs outside repl should throw") +; (catch Throwable t (str/includes? (ex-message t) "add-libs"))) +; +; (with-dynamic-loader +; (binding [*repl* true] +; (is (some #{'org.clojure/data.json} (deps/add-lib 'org.clojure/data.json {:mvn/version "2.4.0"}))))) +; ) diff --git a/test/clojure/test_clojure/rt.clj b/test/clojure/test_clojure/rt.clj index 39526975f8..298fcb00da 100644 --- a/test/clojure/test_clojure/rt.clj +++ b/test/clojure/test_clojure/rt.clj @@ -9,7 +9,8 @@ ; Author: Stuart Halloway (ns clojure.test-clojure.rt - (:require clojure.set) + (:require [clojure.string :as string] + clojure.set) (:use clojure.test clojure.test-helper)) (defn bare-rt-print @@ -75,31 +76,29 @@ (.bindRoot #'example-var 0) (is (not (contains? (meta #'example-var) :macro)))) -(deftest last-var-wins-for-core +(deftest ns-intern-policies (testing "you can replace a core name, with warning" (let [ns (temp-ns) - replacement (gensym)] - (with-err-string-writer (intern ns 'prefers replacement)) + replacement (gensym) + e1 (with-err-string-writer (intern ns 'prefers replacement))] + (is (string/starts-with? e1 "WARNING")) (is (= replacement @('prefers (ns-publics ns)))))) - (testing "you can replace a name you defined before" + (testing "you can replace a defined alias" (let [ns (temp-ns) s (gensym) v1 (intern ns 'foo s) - v2 (intern ns 'bar s)] - (with-err-string-writer (.refer ns 'flatten v1)) - (.refer ns 'flatten v2) + v2 (intern ns 'bar s) + e1 (with-err-string-writer (.refer ns 'flatten v1)) + e2 (with-err-string-writer (.refer ns 'flatten v2))] + (is (string/starts-with? e1 "WARNING")) + (is (string/starts-with? e2 "WARNING")) (is (= v2 (ns-resolve ns 'flatten))))) - (testing "you cannot intern over an existing non-core name" - (let [ns (temp-ns 'clojure.set) - replacement (gensym)] - (is (thrown? IllegalStateException - (intern ns 'subset? replacement))) - (is (nil? ('subset? (ns-publics ns)))) - (is (= #'clojure.set/subset? ('subset? (ns-refers ns)))))) - (testing "you cannot refer over an existing non-core name" - (let [ns (temp-ns 'clojure.set) - replacement (gensym)] - (is (thrown? IllegalStateException - (.refer ns 'subset? #'clojure.set/intersection))) - (is (nil? ('subset? (ns-publics ns)))) - (is (= #'clojure.set/subset? ('subset? (ns-refers ns))))))) + (testing "you cannot replace an interned var" + (let [ns1 (temp-ns) + ns2 (temp-ns) + v1 (intern ns1 'foo 1) + v2 (intern ns2 'foo 2) + e1 (with-err-string-writer (.refer ns1 'foo v2))] + (is (string/starts-with? e1 "REJECTED")) + (is (= v1 (ns-resolve ns1 'foo)))))) + 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 e568b52557..f8f3f5910d 100644 --- a/test/clojure/test_clojure/sequences.clj +++ b/test/clojure/test_clojure/sequences.clj @@ -413,6 +413,49 @@ (next (to-array [(into-array []) nil])) '(nil) (next (to-array [(into-array []) 2 nil])) '(2 nil) )) +(deftest test-nthnext+rest-on-0 + (are [coll] + (and (= (seq coll) (nthnext coll 0)) + (= coll (nthrest coll 0))) + nil + "" + () + '(0) + [] + [0] + #{} + {} + {:a 1} + (range 5))) + +(deftest test-nthnext+rest-on-pos + (are [coll n nthnext-expected nthrest-expected] + (and (= nthnext-expected (nthnext coll n)) + (= nthrest-expected (nthrest coll n))) + + ;coll n nthnext nthrest + nil 1 nil () + "abc" 1 '(\b \c) '(\b \c) + "abc" 3 nil () + "abc" 4 nil () + () 1 nil () + '(1) 1 nil () + '(1) 2 nil () + '(()) 1 nil () + #{} 1 nil () + {:a 1} 1 nil () + [] 1 nil () + [0] 1 nil () + [0] 2 nil () + [[] 2 nil] 1 '(2 nil) '(2 nil) + [[] 2 nil] 2 '(nil) '(nil) + [[] 2 nil] 3 nil () + (sorted-set 1 2 3) 2 '(3) '(3) + (sorted-map :a 1 :b 2) 1 '([:b 2]) '([:b 2]) + (into-array []) 1 nil () + (into-array [1]) 1 nil () + (range 5) 3 '(3 4) '(3 4) + (range 5) 5 nil ())) (deftest test-last (are [x y] (= x y) @@ -781,6 +824,8 @@ (partition 5 [1 2 3]) () + (partition 4 4 [0 0 0] (range 10)) '((0 1 2 3) (4 5 6 7) (8 9 0 0)) + ; (partition 0 [1 2 3]) (repeat nil) ; infinite sequence of nil (partition -1 [1 2 3]) () (partition -2 [1 2 3]) () ) @@ -796,6 +841,25 @@ (is (= (assoc (array-map (repeat 1 :x) :y) '(:x) :z) {'(:x) :z})) (is (= (assoc (hash-map (repeat 1 :x) :y) '(:x) :z) {'(:x) :z}))) +(deftest test-partitionv + (are [x y] (= x y) + (partitionv 2 [1 2 3]) '((1 2)) + (partitionv 2 [1 2 3 4]) '((1 2) (3 4)) + (partitionv 2 []) () + + (partitionv 2 3 [1 2 3 4 5 6 7]) '((1 2) (4 5)) + (partitionv 2 3 [1 2 3 4 5 6 7 8]) '((1 2) (4 5) (7 8)) + (partitionv 2 3 []) () + + (partitionv 1 []) () + (partitionv 1 [1 2 3]) '((1) (2) (3)) + + (partitionv 4 4 [0 0 0] (range 10)) '([0 1 2 3] [4 5 6 7] [8 9 0 0]) + + (partitionv 5 [1 2 3]) () + + (partitionv -1 [1 2 3]) () + (partitionv -2 [1 2 3]) () )) (deftest test-iterate (are [x y] (= x y) @@ -834,7 +898,9 @@ (take 0 [1 2 3 4 5]) () (take -1 [1 2 3 4 5]) () - (take -2 [1 2 3 4 5]) () )) + (take -2 [1 2 3 4 5]) () + + (take 1/4 [1 2 3 4 5]) '(1) )) (deftest test-drop @@ -846,8 +912,52 @@ (drop 0 [1 2 3 4 5]) '(1 2 3 4 5) (drop -1 [1 2 3 4 5]) '(1 2 3 4 5) - (drop -2 [1 2 3 4 5]) '(1 2 3 4 5) )) + (drop -2 [1 2 3 4 5]) '(1 2 3 4 5) + + (drop 1/4 [1 2 3 4 5]) '(2 3 4 5) ) + + (are [coll] (= (drop 4 coll) (drop -2 (drop 4 coll))) + [0 1 2 3 4 5] + (seq [0 1 2 3 4 5]) + (range 6) + (repeat 6 :x)) + ) + +(deftest test-nthrest + (are [x y] (= x y) + (nthrest [1 2 3 4 5] 1) '(2 3 4 5) + (nthrest [1 2 3 4 5] 3) '(4 5) + (nthrest [1 2 3 4 5] 5) () + (nthrest [1 2 3 4 5] 9) () + + (nthrest [1 2 3 4 5] 0) '(1 2 3 4 5) + (nthrest [1 2 3 4 5] -1) '(1 2 3 4 5) + (nthrest [1 2 3 4 5] -2) '(1 2 3 4 5) + + (nthrest [1 2 3 4 5] 1/4) '(2 3 4 5) + (nthrest [1 2 3 4 5] 1.2) '(3 4 5) ) + + ;; (nthrest coll 0) should return coll + (are [coll] (let [r (nthrest coll 0)] (and (= coll r) (= (class coll) (class r)))) + [1 2 3] + (seq [1 2 3]) + (range 10) + (repeat 10 :x) + (seq "abc") )) + +(deftest test-nthnext + (are [x y] (= x y) + (nthnext [1 2 3 4 5] 1) '(2 3 4 5) + (nthnext [1 2 3 4 5] 3) '(4 5) + (nthnext [1 2 3 4 5] 5) nil + (nthnext [1 2 3 4 5] 9) nil + (nthnext [1 2 3 4 5] 0) '(1 2 3 4 5) + (nthnext [1 2 3 4 5] -1) '(1 2 3 4 5) + (nthnext [1 2 3 4 5] -2) '(1 2 3 4 5) + + (nthnext [1 2 3 4 5] 1/4) '(2 3 4 5) + (nthnext [1 2 3 4 5] 1.2) '(3 4 5) )) (deftest test-take-nth (are [x y] (= x y) @@ -986,9 +1096,14 @@ () '(1 2) [] [1 2] {} {:a 1 :b 2} - #{} #{1 2} )) + #{} #{1 2}) + + ; CLJ-2718 + (is (= '(:a) (drop 1 (repeat 2 :a)))) + (is (= () (drop 2 (repeat 2 :a)))) + (is (= () (drop 3 (repeat 2 :a))))) -(defspec longrange-equals-range 100 +(defspec longrange-equals-range 1000 (prop/for-all [start gen/int end gen/int step gen/s-pos-int] @@ -1058,6 +1173,15 @@ (reduce + (iterator-seq (.iterator (range 100)))) 4950 (reduce + (iterator-seq (.iterator (range 0.0 100.0 1.0)))) 4950.0 )) +(deftest range-meta + (are [r] (= r (with-meta r {:a 1})) + (range 10) + (range 5 10) + (range 5 10 1) + (range 10.0) + (range 5.0 10.0) + (range 5.0 10.0 1.0))) + (deftest range-test (let [threads 10 n 1000 @@ -1119,7 +1243,10 @@ {} #{} "" - (into-array []) ) + (into-array []) + (transient []) + (transient #{}) + (transient {})) (are [x] (not (empty? x)) '(1 2) @@ -1128,7 +1255,10 @@ {:a 1 :b 2} #{1 2} "abc" - (into-array [1 2]) )) + (into-array [1 2]) + (transient [1]) + (transient #{1}) + (transient {1 2}))) (deftest test-every? @@ -1331,6 +1461,12 @@ (is (= (partition-all 4 2 [1 2 3 4 5 6 7 8 9]) [[1 2 3 4] [3 4 5 6] [5 6 7 8] [7 8 9] [9]]))) +(deftest test-partitionv-all + (is (= (partitionv-all 4 [1 2 3 4 5 6 7 8 9]) + [[1 2 3 4] [5 6 7 8] [9]])) + (is (= (partitionv-all 4 2 [1 2 3 4 5 6 7 8 9]) + [[1 2 3 4] [3 4 5 6] [5 6 7 8] [7 8 9] [9]]))) + (deftest test-shuffle-invariants (is (= (count (shuffle [1 2 3 4])) 4)) (let [shuffled-seq (shuffle [1 2 3 4])] @@ -1368,3 +1504,150 @@ (deftest test-sort-retains-meta (is (= {:a true} (meta (sort (with-meta (range 10) {:a true}))))) (is (= {:a true} (meta (sort-by :a (with-meta (seq [{:a 5} {:a 2} {:a 3}]) {:a true})))))) + +(deftest test-seqs-implements-iobj + (doseq [coll [[1 2 3] + (vector-of :long 1 2 3) + {:a 1 :b 2 :c 3} + (sorted-map :a 1 :b 2 :c 3) + #{1 2 3} + (sorted-set 1 2 3) + (into clojure.lang.PersistentQueue/EMPTY [1 2 3])]] + (is (= true (instance? clojure.lang.IMeta coll))) + (is (= {:a true} (meta (with-meta coll {:a true})))) + (is (= true (instance? clojure.lang.IMeta (seq coll)))) + (is (= {:a true} (meta (with-meta (seq coll) {:a true})))) + (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 test-reduce-on-coll-seqs + ;; reduce on seq of coll, both with and without an init + (are [coll expected expected-init] + (and + (= expected-init (reduce conj [:init] (seq coll))) + (= expected (reduce conj (seq coll)))) + ;; (seq [ ... ]) + [] [] [:init] + [1] 1 [:init 1] + [[1] 2] [1 2] [:init [1] 2] + + ;; (seq { ... }) + {} [] [:init] + {1 1} [1 1] [:init [1 1]] + {1 1 2 2} [1 1 [2 2]] [:init [1 1] [2 2]] + + ;; (seq (hash-map ... )) + (hash-map) [] [:init] + (hash-map 1 1) [1 1] [:init [1 1]] + (hash-map 1 1 2 2) [1 1 [2 2]] [:init [1 1] [2 2]] + + ;; (seq (sorted-map ... )) + (sorted-map) [] [:init] + (sorted-map 1 1) [1 1] [:init [1 1]] + (sorted-map 1 1 2 2) [1 1 [2 2]] [:init [1 1] [2 2]]) + + (are [coll expected expected-init] + (and + (= expected-init (reduce + 100 (seq coll))) + (= expected (reduce + (seq coll)))) + + ;; (seq (range ...)) + (range 0) 0 100 + (range 1 2) 1 101 + (range 1 3) 3 103)) + +(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..bce7013675 100644 --- a/test/clojure/test_clojure/serialization.clj +++ b/test/clojure/test_clojure/serialization.clj @@ -92,6 +92,7 @@ ; lazy seqs (lazy-seq nil) (lazy-seq (list* (range 50))) + (iterator-seq (.iterator (range 50))) ; transient / persistent! round-trip (build-via-transient []) @@ -182,5 +183,11 @@ (agent nil) ;; stateful seqs - (enumeration-seq (java.util.Collections/enumeration (range 50))) - (iterator-seq (.iterator (range 50))))) \ No newline at end of file + (enumeration-seq (java.util.Collections/enumeration (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/server.clj b/test/clojure/test_clojure/server.clj index 1d24d0b2dd..4d4f20a480 100644 --- a/test/clojure/test_clojure/server.clj +++ b/test/clojure/test_clojure/server.clj @@ -9,6 +9,7 @@ ; Author: Alex Miller (ns clojure.test-clojure.server + (:import java.util.Random) (:require [clojure.test :refer :all]) (:require [clojure.core.server :as s])) @@ -21,9 +22,26 @@ (is (= (ex-data e) opts)) (is (= msg (.getMessage e)))))) +(defn create-random-thread + [] + (Thread. + (fn [] + (let [random (new Random)] + (while (not (.isInterrupted (Thread/currentThread))) + (System/setProperty (Integer/toString (.nextInt random)) (Integer/toString (.nextInt random)))))))) + (deftest test-validate-opts (check-invalid-opts {} "Missing required socket server property :name") (check-invalid-opts {:name "a" :accept 'clojure.core/+} "Missing required socket server property :port") (doseq [port [-1 "5" 999999]] (check-invalid-opts {:name "a" :port port :accept 'clojure.core/+} (str "Invalid socket server port: " port))) (check-invalid-opts {:name "a" :port 5555} "Missing required socket server property :accept")) + +(deftest test-parse-props + (let [thread (create-random-thread)] + (.start thread) + (Thread/sleep 1000) + (try + (is (>= (count + (#'s/parse-props (System/getProperties))) 0)) + (finally (.interrupt thread))))) diff --git a/test/clojure/test_clojure/streams.clj b/test/clojure/test_clojure/streams.clj new file mode 100644 index 0000000000..c5462a1aa0 --- /dev/null +++ b/test/clojure/test_clojure/streams.clj @@ -0,0 +1,103 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +; which can be found in the file epl-v10.html at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(ns clojure.test-clojure.streams + (:use clojure.test) + (:import [java.util.stream Stream LongStream] + [java.util.function Consumer Predicate Supplier])) + +(deftest test-stream-reduce! + (is (= :only-val (stream-reduce! + (.stream [:only-val])))) + (is (= 0 (stream-reduce! + (.stream [])))) + (is (= 5 (stream-reduce! + (.stream [1 4])))) + (is (= 6 (stream-reduce! + (.stream [1 2 3])))) + (is (= [2 3 4] + (stream-reduce! (fn [v i] (conj v (inc i))) + [] + (.stream [1 2 3])))) + + (is (= 15 (stream-reduce! + (LongStream/rangeClosed 1 5)))) + + (is (= 9 (stream-reduce! + (-> (Stream/of (to-array [1 2 3 4 5])) + (.filter (reify Predicate (test [_ v] (odd? v)))))))) + (is (= {:a 1} + (meta + (stream-reduce! (fn [v i] (conj v (inc i))) + (with-meta [] {:a 1}) + (.stream [1 2 3])))))) + +(deftest test-stream-reduced! + (is (= 5 (stream-reduce! + (fn [x y] (reduced (+ x y))) + (.stream [1 4])))) + + (is (= 45 (stream-reduce! + (fn [acc v] + (if (= v 10) + (reduced acc) + (+ acc v))) + (LongStream/rangeClosed 1 10))))) + +(deftest stream-seq!-test + (let [none (.stream []) + one (Stream/of "a") + n (.stream ["a" "b" "c"]) + inf (Stream/generate (reify Supplier (get [_] 42))) + st (stream-seq! one) + l100 (LongStream/range 0 100)] + (is (empty? (map identity (stream-seq! none)))) + (is (seq? st)) + (is (= ["a"] (map identity st))) + (is (= ["a" "b" "c"] (map identity (stream-seq! n)))) + (is (= [42 42 42 42 42] (take 5 (stream-seq! inf)))) + (is (= 4950 (reduce + (stream-seq! l100)))) 4950)) + +(deftest stream-transduce!-test + (let [xf (comp (filter odd?) (take 5))] + (let [st (Stream/of (to-array [1 2 3 4 5 6 7 8 9]))] + (is (= [1 3 5 7 9] (stream-transduce! xf conj st)))) + + (let [inf (Stream/generate (reify Supplier (get [_] 0)))] + (is (empty? (stream-transduce! xf conj (.limit inf 50))))) + + (let [inf (Stream/generate (reify Supplier (get [_] 43)))] + (is (= [43 43 43 43 43] (stream-transduce! xf conj (.limit inf 50))))) + + (let [inf (Stream/generate (reify Supplier (get [_] 43)))] + (is (= 215 (stream-transduce! xf + (.limit inf 50))))) + + (let [inf (Stream/generate (reify Supplier (get [_] 43)))] + (is (= 315 (stream-transduce! xf + 100 (.limit inf 50))))) + + (let [inf (Stream/generate (reify Supplier (get [_] 43)))] + (is (= "4343434343" (stream-transduce! xf str (.limit inf 50))))))) + +(deftest stream-into!-test + (let [none (.stream []) + one (Stream/of "a") + n (.stream ["a" "b" "c"]) + inf (Stream/generate (reify Supplier (get [_] 42))) + par (-> (LongStream/rangeClosed 1 10) .boxed .parallel) + xf (comp (map #(+ 2 %)) (filter odd?)) + par2 (-> (LongStream/rangeClosed 1 10) .boxed .parallel)] + (is (empty? (stream-into! [] none))) + (is (= ["a"] (stream-into! [] one))) + (is (= ["a" "b" "c"] (stream-into! [] n))) + (is (= [42 42 42 42 42] + (stream-into! [] (.limit inf 5)))) + (is (= [1 2 3 4 5 6 7 8 9 10] + (stream-into! [] par))) + (is (= {:a 1} + (meta + (stream-into! (with-meta [] {:a 1}) + (.stream [1 2 3]))))) + (is (= {:a 1} + (meta + (stream-into! (with-meta clojure.lang.PersistentQueue/EMPTY {:a 1}) + (.stream [1 2 3]))))) + (is (= [-1 -2 3 5 7 9 11] (stream-into! [-1 -2] xf par2))))) 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..97cc0c5add 100644 --- a/test/clojure/test_clojure/vectors.clj +++ b/test/clojure/test_clojure/vectors.clj @@ -9,7 +9,11 @@ ; Author: Stuart Halloway, Daniel Solano Gómez (ns clojure.test-clojure.vectors - (:use clojure.test)) + (:use clojure.test) + (:import + [java.util Collection Spliterator] + [java.util.function Consumer] + [java.util.stream Collectors])) (deftest test-reversed-vec (let [r (range 6) @@ -416,3 +420,73 @@ (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]])))) + +(set! *warn-on-reflection* true) + +;; remember returns a consumer that adds to an-atom of coll +(defn remember + ^Consumer [an-atom] + (reify Consumer (accept [_ v] (swap! an-atom conj v)))) + +(deftest test-empty-vector-spliterator + (let [v [] + s (.spliterator ^Collection v) + seen (atom [])] + (is (= 0 (.estimateSize s) (.getExactSizeIfKnown s))) + (is (nil? (.trySplit s))) + (is (false? (.tryAdvance s (remember seen)))) + (is (= @seen [])))) + +;; tryAdvance then forEachRemaining walks vector spliterator +(deftest test-spliterator-tryadvance-then-forEach + (let [n 66 + source-vec (vec (range n))] + (for [v (into [(vec (range n))] (map #(subvec source-vec % (+ % 33)) (range 33)))] + (dotimes [up-to n] + (let [s (.spliterator ^Collection v) + seen (atom []) + consumer (remember seen)] + (loop [i 0] + (if (< i up-to) + (do (is (true? (.tryAdvance s consumer))) (recur (inc i))) + (.forEachRemaining s consumer))) + (is (= v @seen)) + (is (false? (.tryAdvance s consumer)))))))) + +;; recursively split vector spliterators, walk all of the splits +(deftest test-spliterator-trySplit + (dotimes [n 257] + (let [v (vec (range n)) + seen (atom #{}) + consumer (remember seen) + splits (loop [ss [(.spliterator ^Collection v)]] + (let [ss' (map #(.trySplit ^Spliterator %) ss)] + (if (every? nil? ss') + ss + (recur (into ss (remove nil? ss'))))))] + (loop [[spl & sr] splits] + (when spl + (.forEachRemaining ^Spliterator spl consumer) + (recur sr))) + (is (= v (sort @seen)))))) + +(deftest test-vector-parallel-stream + (dotimes [n 1024] + (let [v (vec (range n))] + (is (= n + (-> ^Collection v .stream (.collect (Collectors/counting))) + (-> ^Collection v .parallelStream (.collect (Collectors/counting))) + (-> v ^Collection (subvec 0 n) .stream (.collect (Collectors/counting))) + (-> v ^Collection (subvec 0 n) .parallelStream (.collect (Collectors/counting)))))))) \ No newline at end of file diff --git a/test/java/clojure/test/AdapterExerciser.java b/test/java/clojure/test/AdapterExerciser.java new file mode 100644 index 0000000000..5e2758b1d7 --- /dev/null +++ b/test/java/clojure/test/AdapterExerciser.java @@ -0,0 +1,499 @@ + +package clojure.test; + +public class AdapterExerciser { + @FunctionalInterface + public interface L { + public long takesRetL(); + } + @FunctionalInterface + public interface I { + public int takesRetI(); + } + @FunctionalInterface + public interface S { + public short takesRetS(); + } + @FunctionalInterface + public interface B { + public byte takesRetB(); + } + @FunctionalInterface + public interface D { + public double takesRetD(); + } + @FunctionalInterface + public interface F { + public float takesRetF(); + } + @FunctionalInterface + public interface O { + public AdapterExerciser takesRetO(); + } + @FunctionalInterface + public interface LL { + public long takesLRetL(long a); + } + @FunctionalInterface + public interface DL { + public long takesDRetL(double a); + } + @FunctionalInterface + public interface OL { + public long takesORetL(AdapterExerciser a); + } + @FunctionalInterface + public interface LI { + public int takesLRetI(long a); + } + @FunctionalInterface + public interface DI { + public int takesDRetI(double a); + } + @FunctionalInterface + public interface OI { + public int takesORetI(AdapterExerciser a); + } + @FunctionalInterface + public interface LS { + public short takesLRetS(long a); + } + @FunctionalInterface + public interface DS { + public short takesDRetS(double a); + } + @FunctionalInterface + public interface OS { + public short takesORetS(AdapterExerciser a); + } + @FunctionalInterface + public interface LB { + public byte takesLRetB(long a); + } + @FunctionalInterface + public interface DB { + public byte takesDRetB(double a); + } + @FunctionalInterface + public interface OB { + public byte takesORetB(AdapterExerciser a); + } + @FunctionalInterface + public interface LD { + public double takesLRetD(long a); + } + @FunctionalInterface + public interface DD { + public double takesDRetD(double a); + } + @FunctionalInterface + public interface OD { + public double takesORetD(AdapterExerciser a); + } + @FunctionalInterface + public interface LF { + public float takesLRetF(long a); + } + @FunctionalInterface + public interface DF { + public float takesDRetF(double a); + } + @FunctionalInterface + public interface OF { + public float takesORetF(AdapterExerciser a); + } + @FunctionalInterface + public interface LO { + public AdapterExerciser takesLRetO(long a); + } + @FunctionalInterface + public interface DO { + public AdapterExerciser takesDRetO(double a); + } + @FunctionalInterface + public interface OO { + public AdapterExerciser takesORetO(AdapterExerciser a); + } + @FunctionalInterface + public interface LLL { + public long takesLLRetL(long a, long b); + } + @FunctionalInterface + public interface LOL { + public long takesLORetL(long a, AdapterExerciser b); + } + @FunctionalInterface + public interface OLL { + public long takesOLRetL(AdapterExerciser a, long b); + } + @FunctionalInterface + public interface DDL { + public long takesDDRetL(double a, double b); + } + @FunctionalInterface + public interface LDL { + public long takesLDRetL(long a, double b); + } + @FunctionalInterface + public interface DLL { + public long takesDLRetL(double a, long b); + } + @FunctionalInterface + public interface OOL { + public long takesOORetL(AdapterExerciser a, AdapterExerciser b); + } + @FunctionalInterface + public interface ODL { + public long takesODRetL(AdapterExerciser a, double b); + } + @FunctionalInterface + public interface DOL { + public long takesDORetL(double a, AdapterExerciser b); + } + @FunctionalInterface + public interface LLI { + public int takesLLRetI(long a, long b); + } + @FunctionalInterface + public interface LOI { + public int takesLORetI(long a, AdapterExerciser b); + } + @FunctionalInterface + public interface OLI { + public int takesOLRetI(AdapterExerciser a, long b); + } + @FunctionalInterface + public interface DDI { + public int takesDDRetI(double a, double b); + } + @FunctionalInterface + public interface LDI { + public int takesLDRetI(long a, double b); + } + @FunctionalInterface + public interface DLI { + public int takesDLRetI(double a, long b); + } + @FunctionalInterface + public interface OOI { + public int takesOORetI(AdapterExerciser a, AdapterExerciser b); + } + @FunctionalInterface + public interface ODI { + public int takesODRetI(AdapterExerciser a, double b); + } + @FunctionalInterface + public interface DOI { + public int takesDORetI(double a, AdapterExerciser b); + } + @FunctionalInterface + public interface LLS { + public short takesLLRetS(long a, long b); + } + @FunctionalInterface + public interface LOS { + public short takesLORetS(long a, AdapterExerciser b); + } + @FunctionalInterface + public interface OLS { + public short takesOLRetS(AdapterExerciser a, long b); + } + @FunctionalInterface + public interface DDS { + public short takesDDRetS(double a, double b); + } + @FunctionalInterface + public interface LDS { + public short takesLDRetS(long a, double b); + } + @FunctionalInterface + public interface DLS { + public short takesDLRetS(double a, long b); + } + @FunctionalInterface + public interface OOS { + public short takesOORetS(AdapterExerciser a, AdapterExerciser b); + } + @FunctionalInterface + public interface ODS { + public short takesODRetS(AdapterExerciser a, double b); + } + @FunctionalInterface + public interface DOS { + public short takesDORetS(double a, AdapterExerciser b); + } + @FunctionalInterface + public interface LLB { + public byte takesLLRetB(long a, long b); + } + @FunctionalInterface + public interface LOB { + public byte takesLORetB(long a, AdapterExerciser b); + } + @FunctionalInterface + public interface OLB { + public byte takesOLRetB(AdapterExerciser a, long b); + } + @FunctionalInterface + public interface DDB { + public byte takesDDRetB(double a, double b); + } + @FunctionalInterface + public interface LDB { + public byte takesLDRetB(long a, double b); + } + @FunctionalInterface + public interface DLB { + public byte takesDLRetB(double a, long b); + } + @FunctionalInterface + public interface OOB { + public byte takesOORetB(AdapterExerciser a, AdapterExerciser b); + } + @FunctionalInterface + public interface ODB { + public byte takesODRetB(AdapterExerciser a, double b); + } + @FunctionalInterface + public interface DOB { + public byte takesDORetB(double a, AdapterExerciser b); + } + @FunctionalInterface + public interface LLD { + public double takesLLRetD(long a, long b); + } + @FunctionalInterface + public interface LOD { + public double takesLORetD(long a, AdapterExerciser b); + } + @FunctionalInterface + public interface OLD { + public double takesOLRetD(AdapterExerciser a, long b); + } + @FunctionalInterface + public interface DDD { + public double takesDDRetD(double a, double b); + } + @FunctionalInterface + public interface LDD { + public double takesLDRetD(long a, double b); + } + @FunctionalInterface + public interface DLD { + public double takesDLRetD(double a, long b); + } + @FunctionalInterface + public interface OOD { + public double takesOORetD(AdapterExerciser a, AdapterExerciser b); + } + @FunctionalInterface + public interface ODD { + public double takesODRetD(AdapterExerciser a, double b); + } + @FunctionalInterface + public interface DOD { + public double takesDORetD(double a, AdapterExerciser b); + } + @FunctionalInterface + public interface LLF { + public float takesLLRetF(long a, long b); + } + @FunctionalInterface + public interface LOF { + public float takesLORetF(long a, AdapterExerciser b); + } + @FunctionalInterface + public interface OLF { + public float takesOLRetF(AdapterExerciser a, long b); + } + @FunctionalInterface + public interface DDF { + public float takesDDRetF(double a, double b); + } + @FunctionalInterface + public interface LDF { + public float takesLDRetF(long a, double b); + } + @FunctionalInterface + public interface DLF { + public float takesDLRetF(double a, long b); + } + @FunctionalInterface + public interface OOF { + public float takesOORetF(AdapterExerciser a, AdapterExerciser b); + } + @FunctionalInterface + public interface ODF { + public float takesODRetF(AdapterExerciser a, double b); + } + @FunctionalInterface + public interface DOF { + public float takesDORetF(double a, AdapterExerciser b); + } + @FunctionalInterface + public interface LLO { + public AdapterExerciser takesLLRetO(long a, long b); + } + @FunctionalInterface + public interface LOO { + public AdapterExerciser takesLORetO(long a, AdapterExerciser b); + } + @FunctionalInterface + public interface OLO { + public AdapterExerciser takesOLRetO(AdapterExerciser a, long b); + } + @FunctionalInterface + public interface DDO { + public AdapterExerciser takesDDRetO(double a, double b); + } + @FunctionalInterface + public interface LDO { + public AdapterExerciser takesLDRetO(long a, double b); + } + @FunctionalInterface + public interface DLO { + public AdapterExerciser takesDLRetO(double a, long b); + } + @FunctionalInterface + public interface OOO { + public AdapterExerciser takesOORetO(AdapterExerciser a, AdapterExerciser b); + } + @FunctionalInterface + public interface ODO { + public AdapterExerciser takesODRetO(AdapterExerciser a, double b); + } + @FunctionalInterface + public interface DOO { + public AdapterExerciser takesDORetO(double a, AdapterExerciser b); + } + @FunctionalInterface + public interface OOOO { + public AdapterExerciser takesOOORetO(AdapterExerciser a, AdapterExerciser b, AdapterExerciser c); + } + @FunctionalInterface + public interface OOOOO { + public AdapterExerciser takesOOOORetO(AdapterExerciser a, AdapterExerciser b, AdapterExerciser c, AdapterExerciser d); + } + @FunctionalInterface + public interface OOOOOO { + public AdapterExerciser takesOOOOORetO(AdapterExerciser a, AdapterExerciser b, AdapterExerciser c, AdapterExerciser d, AdapterExerciser e); + } + @FunctionalInterface + public interface OOOOOOO { + public AdapterExerciser takesOOOOOORetO(AdapterExerciser a, AdapterExerciser b, AdapterExerciser c, AdapterExerciser d, AdapterExerciser e, AdapterExerciser f); + } + @FunctionalInterface + public interface OOOOOOOO { + public AdapterExerciser takesOOOOOOORetO(AdapterExerciser a, AdapterExerciser b, AdapterExerciser c, AdapterExerciser d, AdapterExerciser e, AdapterExerciser f, AdapterExerciser g); + } + @FunctionalInterface + public interface OOOOOOOOO { + public AdapterExerciser takesOOOOOOOORetO(AdapterExerciser a, AdapterExerciser b, AdapterExerciser c, AdapterExerciser d, AdapterExerciser e, AdapterExerciser f, AdapterExerciser g, AdapterExerciser h); + } + @FunctionalInterface + public interface OOOOOOOOOO { + public AdapterExerciser takesOOOOOOOOORetO(AdapterExerciser a, AdapterExerciser b, AdapterExerciser c, AdapterExerciser d, AdapterExerciser e, AdapterExerciser f, AdapterExerciser g, AdapterExerciser h, AdapterExerciser i); + } + @FunctionalInterface + public interface OOOOOOOOOOO { + public AdapterExerciser takesOOOOOOOOOORetO(AdapterExerciser a, AdapterExerciser b, AdapterExerciser c, AdapterExerciser d, AdapterExerciser e, AdapterExerciser f, AdapterExerciser g, AdapterExerciser h, AdapterExerciser i, AdapterExerciser j); + } + public String methodL(L a) { return "L"; } + public String methodI(I a) { return "I"; } + public String methodS(S a) { return "S"; } + public String methodB(B a) { return "B"; } + public String methodD(D a) { return "D"; } + public String methodF(F a) { return "F"; } + public String methodO(O a) { return "O"; } + public String methodLL(LL a) { return "LL"; } + public String methodDL(DL a) { return "DL"; } + public String methodOL(OL a) { return "OL"; } + public String methodLI(LI a) { return "LI"; } + public String methodDI(DI a) { return "DI"; } + public String methodOI(OI a) { return "OI"; } + public String methodLS(LS a) { return "LS"; } + public String methodDS(DS a) { return "DS"; } + public String methodOS(OS a) { return "OS"; } + public String methodLB(LB a) { return "LB"; } + public String methodDB(DB a) { return "DB"; } + public String methodOB(OB a) { return "OB"; } + public String methodLD(LD a) { return "LD"; } + public String methodDD(DD a) { return "DD"; } + public String methodOD(OD a) { return "OD"; } + public String methodLF(LF a) { return "LF"; } + public String methodDF(DF a) { return "DF"; } + public String methodOF(OF a) { return "OF"; } + public String methodLO(LO a) { return "LO"; } + public String methodDO(DO a) { return "DO"; } + public String methodOO(OO a) { return "OO"; } + public String methodLLL(LLL a) { return "LLL"; } + public String methodLOL(LOL a) { return "LOL"; } + public String methodOLL(OLL a) { return "OLL"; } + public String methodDDL(DDL a) { return "DDL"; } + public String methodLDL(LDL a) { return "LDL"; } + public String methodDLL(DLL a) { return "DLL"; } + public String methodOOL(OOL a) { return "OOL"; } + public String methodODL(ODL a) { return "ODL"; } + public String methodDOL(DOL a) { return "DOL"; } + public String methodLLI(LLI a) { return "LLI"; } + public String methodLOI(LOI a) { return "LOI"; } + public String methodOLI(OLI a) { return "OLI"; } + public String methodDDI(DDI a) { return "DDI"; } + public String methodLDI(LDI a) { return "LDI"; } + public String methodDLI(DLI a) { return "DLI"; } + public String methodOOI(OOI a) { return "OOI"; } + public String methodODI(ODI a) { return "ODI"; } + public String methodDOI(DOI a) { return "DOI"; } + public String methodLLS(LLS a) { return "LLS"; } + public String methodLOS(LOS a) { return "LOS"; } + public String methodOLS(OLS a) { return "OLS"; } + public String methodDDS(DDS a) { return "DDS"; } + public String methodLDS(LDS a) { return "LDS"; } + public String methodDLS(DLS a) { return "DLS"; } + public String methodOOS(OOS a) { return "OOS"; } + public String methodODS(ODS a) { return "ODS"; } + public String methodDOS(DOS a) { return "DOS"; } + public String methodLLB(LLB a) { return "LLB"; } + public String methodLOB(LOB a) { return "LOB"; } + public String methodOLB(OLB a) { return "OLB"; } + public String methodDDB(DDB a) { return "DDB"; } + public String methodLDB(LDB a) { return "LDB"; } + public String methodDLB(DLB a) { return "DLB"; } + public String methodOOB(OOB a) { return "OOB"; } + public String methodODB(ODB a) { return "ODB"; } + public String methodDOB(DOB a) { return "DOB"; } + public String methodLLD(LLD a) { return "LLD"; } + public String methodLOD(LOD a) { return "LOD"; } + public String methodOLD(OLD a) { return "OLD"; } + public String methodDDD(DDD a) { return "DDD"; } + public String methodLDD(LDD a) { return "LDD"; } + public String methodDLD(DLD a) { return "DLD"; } + public String methodOOD(OOD a) { return "OOD"; } + public String methodODD(ODD a) { return "ODD"; } + public String methodDOD(DOD a) { return "DOD"; } + public String methodLLF(LLF a) { return "LLF"; } + public String methodLOF(LOF a) { return "LOF"; } + public String methodOLF(OLF a) { return "OLF"; } + public String methodDDF(DDF a) { return "DDF"; } + public String methodLDF(LDF a) { return "LDF"; } + public String methodDLF(DLF a) { return "DLF"; } + public String methodOOF(OOF a) { return "OOF"; } + public String methodODF(ODF a) { return "ODF"; } + public String methodDOF(DOF a) { return "DOF"; } + public String methodLLO(LLO a) { return "LLO"; } + public String methodLOO(LOO a) { return "LOO"; } + public String methodOLO(OLO a) { return "OLO"; } + public String methodDDO(DDO a) { return "DDO"; } + public String methodLDO(LDO a) { return "LDO"; } + public String methodDLO(DLO a) { return "DLO"; } + public String methodOOO(OOO a) { return "OOO"; } + public String methodODO(ODO a) { return "ODO"; } + public String methodDOO(DOO a) { return "DOO"; } + public String methodOOOO(OOOO a) { return "OOOO"; } + public String methodOOOOO(OOOOO a) { return "OOOOO"; } + public String methodOOOOOO(OOOOOO a) { return "OOOOOO"; } + public String methodOOOOOOO(OOOOOOO a) { return "OOOOOOO"; } + public String methodOOOOOOOO(OOOOOOOO a) { return "OOOOOOOO"; } + public String methodOOOOOOOOO(OOOOOOOOO a) { return "OOOOOOOOO"; } + public String methodOOOOOOOOOO(OOOOOOOOOO a) { return "OOOOOOOOOO"; } + public String methodOOOOOOOOOOO(OOOOOOOOOOO a) { return "OOOOOOOOOOO"; }} \ No newline at end of file diff --git a/test/java/clojure/test/ConcreteClass.java b/test/java/clojure/test/ConcreteClass.java new file mode 100644 index 0000000000..69dde53538 --- /dev/null +++ b/test/java/clojure/test/ConcreteClass.java @@ -0,0 +1,8 @@ +package clojure.test; + +public class ConcreteClass implements GenericInterface { + @Override + public Integer stampWidgets(Integer val) { + return 42; + } +} \ No newline at end of file diff --git a/test/java/clojure/test/FIConstructor.java b/test/java/clojure/test/FIConstructor.java new file mode 100644 index 0000000000..42a4e6caa3 --- /dev/null +++ b/test/java/clojure/test/FIConstructor.java @@ -0,0 +1,17 @@ +package clojure.test; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; + +public class FIConstructor { + + public List numbers; + + public FIConstructor(Predicate pred) { + List numbers = Arrays.asList(-2, -1, 0, 1, 2); + Object[] filteredNumbers = numbers.stream().filter(pred).toArray(); + this.numbers = Arrays.asList(filteredNumbers); + } + +} diff --git a/test/java/clojure/test/FIStatic.java b/test/java/clojure/test/FIStatic.java new file mode 100644 index 0000000000..8273c357ee --- /dev/null +++ b/test/java/clojure/test/FIStatic.java @@ -0,0 +1,26 @@ +package clojure.test; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; + +public class FIStatic { + + public static List numbers(Predicate pred) { + List numbers = Arrays.asList(-2, -1, 0, 1, 2); + Object[] filteredNumbers = numbers.stream().filter(pred).toArray(); + return Arrays.asList(filteredNumbers); + } + + public static String allowsNullFI(Predicate pred) { + if(pred == null) { + return "null"; + } else { + if(pred.test("abc")) { + return "pass"; + } else { + return "fail"; + } + } + } +} diff --git a/test/java/clojure/test/FunctionalTester.java b/test/java/clojure/test/FunctionalTester.java new file mode 100644 index 0000000000..7b5383cc30 --- /dev/null +++ b/test/java/clojure/test/FunctionalTester.java @@ -0,0 +1,31 @@ +package clojure.test; + +public class FunctionalTester { + public char testVar; + + @FunctionalInterface + public interface FI { + char action(String str, int pos); + } + + public FunctionalTester(String str, int pos, FI fi) { + testVar = fi.action(str, pos); + } + + public void instanceMethodWithFIArg(String str, int pos, FI fi) { + testVar = fi.action(str, pos); + } + + public static char staticMethodWithFIArg(String str, int pos, FI fi) { + return fi.action(str, pos); + } + + public static char getChar(String str, int pos) { + return str.charAt(pos); + } + + public static char getChar(int value, long n) { + return "Fail".charAt(0); + } + +} \ No newline at end of file diff --git a/test/java/clojure/test/GenericInterface.java b/test/java/clojure/test/GenericInterface.java new file mode 100644 index 0000000000..a20496ab1a --- /dev/null +++ b/test/java/clojure/test/GenericInterface.java @@ -0,0 +1,5 @@ +package clojure.test; + +public interface GenericInterface { + T stampWidgets(T val); +} \ No newline at end of file diff --git a/test/java/clojure/test/SwissArmy.java b/test/java/clojure/test/SwissArmy.java new file mode 100644 index 0000000000..f38e2d44f5 --- /dev/null +++ b/test/java/clojure/test/SwissArmy.java @@ -0,0 +1,45 @@ +package clojure.test; + +import clojure.java.api.Clojure; +import clojure.lang.IFn; + +public class SwissArmy { + public static String doppelganger = "static-field"; + public String ctorId; + public static IFn idFn = Clojure.var("clojure.core", "identity"); + + public SwissArmy() {this.ctorId = "1";} + public SwissArmy(int a, long b) {this.ctorId = "2";} + public SwissArmy(long a, int b) {this.ctorId = "3";} + public SwissArmy(boolean a, boolean b) {this.ctorId = "4";} + public SwissArmy(long[] a, int b) {this.ctorId = "5";} + public SwissArmy(String[] a, long b) {this.ctorId = "6";} + public SwissArmy(int[][] a, long b) {this.ctorId = "7";} + + public String noArgs() {return "";} + public String twoArgsIL(int a, long b) {return "int-long";} + public String twoArgsLI(long a, int b) {return "long-int";} + public String twoArgsBB(boolean a, boolean b) {return "boolean-boolean";} +// public String twoArgsLAI(long[] a, int b) {return "long<>-int";} + public String twoArgsSAL(String[] a, long b) {return "java.lang.String<>-long";} +// public String twoArgsMDIL(int[][] a, long b) {return "int<><>-long";} + public String arityOverloadMethod(int a) {return "int";} + public String arityOverloadMethod(int a, int b) {return "int-int";} + public String arityOverloadMethod(int a, int b, int c) {return "int-int-int";} + public String doppelganger(int a, int b) {return "int-int";} + + public static String staticNoArgs() {return "";} + public static String staticOneArg(boolean a) {return "boolean";} + public static String staticTwoArgsIL(int a, long b) {return "int-long";} + public static String staticTwoArgsLI(long a, int b) {return "long-int";} + public static String staticTwoArgsBB(boolean a, boolean b) {return "boolean-boolean";} + public static String staticTwoArgsSAL(String[] a, long b) {return "java.lang.String<>-long";} +// public static String staticTwoArgsMDIL(int[][] a, long b) {return "int<><>-long";} +// public static String couldReflect(long[] a, int b) {return "long<>-int";} + public static String couldReflect(Object[] a, int b) {return "java.lang.Object<>-int";} + public static String staticArityOverloadMethod(int a) {return "int";} + public static String staticArityOverloadMethod(int a, int b) {return "int-int";} + public static String staticArityOverloadMethod(int a, int b, int c) {return "int-int-int";} + public static String doppelganger(int a, int b, long c) {return "int-int-long";} + public static String doppelganger() {return "";} +}