diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 766c05f631..8a2d87838c 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -27,15 +27,15 @@ jobs:
java-version: 21
distribution: 'temurin'
cache: 'maven'
- server-id: sonatype-nexus-staging
+ server-id: central
server-username: MAVEN_USERNAME
server-password: MAVEN_PASSWORD
- gpg-private-key: ${{ secrets.OSSRH_GPG_SECRET_KEY }}
+ gpg-private-key: ${{ secrets.CENTRAL_GPG_SECRET_KEY }}
gpg-passphrase: GPG_PASSPHRASE
- name: Release
run: script/build
env:
HUDSON: ${{ github.event.inputs.deploy }}
- MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }}
- MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
- GPG_PASSPHRASE: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }}
+ MAVEN_USERNAME: ${{ secrets.CENTRAL_USERNAME }}
+ MAVEN_PASSWORD: ${{ secrets.CENTRAL_PASSWORD }}
+ GPG_PASSPHRASE: ${{ secrets.CENTRAL_GPG_SECRET_KEY_PASSWORD }}
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index e6a4590d05..b8c5ae94ba 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -5,7 +5,7 @@ jobs:
# Runtime Tests
runtime-test:
name: Runtime Tests
- runs-on: macos-14
+ runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
@@ -19,50 +19,81 @@ jobs:
tools-deps: '1.10.1.763'
- name: Cache maven
- uses: actions/cache@v4
+ uses: actions/cache@v4.2.0
env:
cache-name: cache-maven
with:
path: ~/.m2
- key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }}
+ key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('deps.edn', '*/deps.edn') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
- name: Cache gitlibs
- uses: actions/cache@v4
+ uses: actions/cache@v4.2.0
env:
cache-name: cache-gitlibs
with:
path: ~/.gitlibs
- key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }}
+ key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('deps.edn', '*/deps.edn') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
- # - name: Cache JSC
- # uses: actions/cache@v4
- # env:
- # cache-name: cache-jsc
- # with:
- # path: WebKit
- # key: ${{ runner.os }}-jsc
- # restore-keys: |
- # ${{ runner.os }}-jsc
-
- name: Build tests
run: clojure -M:runtime.test.build
- # - name: Install JSC
- # run: ./ci/install_jsc.sh
+ - name: Run tests
+ run: |
+ node builds/out-adv/core-advanced-test.js | tee test-out.txt
+ grep -qxF '0 failures, 0 errors.' test-out.txt
+
+ # Lite Tests
+ lite-test:
+ name: Lite Tests
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+
+ - uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '21'
+
+ - uses: DeLaGuardo/setup-clojure@3.1
+ with:
+ tools-deps: '1.10.1.763'
+
+ - name: Cache maven
+ uses: actions/cache@v4.2.0
+ env:
+ cache-name: cache-maven
+ with:
+ path: ~/.m2
+ key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('deps.edn', '*/deps.edn') }}
+ restore-keys: |
+ ${{ runner.os }}-${{ env.cache-name }}-
+
+ - name: Cache gitlibs
+ uses: actions/cache@v4.2.0
+ env:
+ cache-name: cache-gitlibs
+ with:
+ path: ~/.gitlibs
+ key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('deps.edn', '*/deps.edn') }}
+ restore-keys: |
+ ${{ runner.os }}-${{ env.cache-name }}-
+
+ - name: Build tests
+ run: clojure -M:lite.test.build
- name: Run tests
run: |
- /System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Helpers/jsc builds/out-adv/core-advanced-test.js | tee test-out.txt
+ node builds/out-lite/lite-test.js | tee test-out.txt
grep -qxF '0 failures, 0 errors.' test-out.txt
# Runtime Tests
runtime-windows-test:
name: Runtime Windows Tests
- runs-on: windows-2019
+ runs-on: windows-2022
steps:
- uses: actions/checkout@v2
@@ -88,7 +119,7 @@ jobs:
# Self-host Tests
self-host-test:
name: Self-host Tests
- runs-on: macos-14
+ runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
@@ -102,22 +133,22 @@ jobs:
tools-deps: '1.10.1.763'
- name: Cache maven
- uses: actions/cache@v4
+ uses: actions/cache@v4.2.0
env:
cache-name: cache-maven
with:
path: ~/.m2
- key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }}
+ key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('deps.edn', '*/deps.edn') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
- name: Cache gitlibs
- uses: actions/cache@v4
+ uses: actions/cache@v4.2.0
env:
cache-name: cache-gitlibs
with:
path: ~/.gitlibs
- key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }}
+ key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('deps.edn', '*/deps.edn') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
@@ -132,7 +163,7 @@ jobs:
# Self-parity Tests
self-parity-test:
name: Self-parity Tests
- runs-on: macos-14
+ runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
@@ -146,22 +177,22 @@ jobs:
tools-deps: '1.10.1.763'
- name: Cache maven
- uses: actions/cache@v4
+ uses: actions/cache@v4.2.0
env:
cache-name: cache-maven
with:
path: ~/.m2
- key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }}
+ key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('deps.edn', '*/deps.edn') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
- name: Cache gitlibs
- uses: actions/cache@v4
+ uses: actions/cache@v4.2.0
env:
cache-name: cache-gitlibs
with:
path: ~/.gitlibs
- key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }}
+ key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('deps.edn', '*/deps.edn') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
@@ -176,7 +207,7 @@ jobs:
# Compiler Tests
compiler-test:
name: Compiler Tests
- runs-on: macos-14
+ runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
@@ -190,22 +221,22 @@ jobs:
tools-deps: '1.10.1.763'
- name: Cache maven
- uses: actions/cache@v4
+ uses: actions/cache@v4.2.0
env:
cache-name: cache-maven
with:
path: ~/.m2
- key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }}
+ key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('deps.edn', '*/deps.edn') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
- name: Cache gitlibs
- uses: actions/cache@v4
+ uses: actions/cache@v4.2.0
env:
cache-name: cache-gitlibs
with:
path: ~/.gitlibs
- key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }}
+ key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('deps.edn', '*/deps.edn') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
@@ -215,7 +246,7 @@ jobs:
# Compiler Windows Tests
compiler-windows-test:
name: Compiler Windows Tests
- runs-on: windows-2019
+ runs-on: windows-2022
steps:
- uses: actions/checkout@v2
@@ -235,7 +266,7 @@ jobs:
# CLI Tests
cli-test:
name: CLI Tests
- runs-on: macos-14
+ runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
@@ -251,22 +282,22 @@ jobs:
tools-deps: '1.10.1.763'
- name: Cache maven
- uses: actions/cache@v4
+ uses: actions/cache@v4.2.0
env:
cache-name: cache-maven
with:
path: ~/.m2
- key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }}
+ key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('deps.edn', '*/deps.edn') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
- name: Cache gitlibs
- uses: actions/cache@v4
+ uses: actions/cache@v4.2.0
env:
cache-name: cache-gitlibs
with:
path: ~/.gitlibs
- key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/deps.edn') }}
+ key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('deps.edn', '*/deps.edn') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
diff --git a/README.md b/README.md
index 98323b88b8..2975a3f157 100644
--- a/README.md
+++ b/README.md
@@ -6,20 +6,20 @@ Official web site: https://clojurescript.org
## Releases and dependency information ##
-Latest stable release: 1.12.42
+Latest stable release: 1.12.134
* [All released versions](https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22)
[Clojure deps.edn](http://clojure.org/guides/deps_and_cli) dependency information:
```
- org.clojure/clojurescript {:mvn/version "1.12.42"}
+ org.clojure/clojurescript {:mvn/version "1.12.134"}
```
[Leiningen](https://github.com/technomancy/leiningen/) dependency information:
```
-[org.clojure/clojurescript "1.12.42"]
+[org.clojure/clojurescript "1.12.134"]
```
[Maven](https://maven.apache.org) dependency information:
@@ -28,7 +28,7 @@ Latest stable release: 1.12.42
org.clojure
clojurescript
- 1.12.38
+ 1.12.134
```
@@ -45,7 +45,7 @@ Please point all of your questions and feedback to the
[Clojure mailing list](https://groups.google.com/group/clojure). There
is a community run
[ClojureScript user mailing list](https://groups.google.com/group/clojurescript) and
-the IRC channel, `#clojurescript` on [freenode.net](https://freenode.net/), is quite active.
+the IRC channel, `#clojurescript` on [freenode.net](https://freenode.net/), is quite active.
There is also a community run [Slack channel](https://clojurians.slack.com). The
Jira bug/feature tracking application is located at
. Before submitting issues
diff --git a/changes.md b/changes.md
index 4e616fb81a..54c63d8051 100644
--- a/changes.md
+++ b/changes.md
@@ -1,3 +1,41 @@
+## 1.12.134
+
+### Changes
+* Be less specific about the behavior of integer coercion fns
+* Provide `cljs.proxy/proxy` default
+* `cljs.proxy/builder`, `cache-fn` parameterization
+
+### Fixes
+* `cljs.proxy` doesn't handle `for .. of` correctly
+* Docstrings for `:lite-mode` support fns
+* CLJS-3466: support qualified method in return position
+* CLJS-3464: `parents` does not walk JavaScript prototype chain
+* CLJS-3456: bootstrap wasn't updated for cljs.compiler/emit-global-export change
+* CLJS-3463: rename all the lite mode data structures / fns to avoid clashing
+
+## 1.12.116
+
+### Changes
+* CLJS-3233: `:refer-global` + `:only`, `:require-global`
+* CLJS-3451: make munge-str public
+* various small DCE enhancements
+* browser REPL reuses same window
+
+### Enhancements
+* Clojure method values syntax support
+* `cljs.proxy`, experimental namespace for efficient interop
+* CLJS-2471: ChunkedSeq should implemented ICounted
+* CLJS-3452: optimize str by compiling to + / .toString + compile time optimizations
+* `:lite-mode` and `:elide-to-string`, new experimental compiler flags for smaller artifacts
+* CLJS-3439: REPL doc support for externs
+
+### Fixes
+* Fix REPL load-file issue
+* CLJS-3425: Incorrect handling of ##NaN with min/max
+* CLJS-3461: don't hard-code destructuring to PAM
+* CLJS-3454: New set instances are created when redundant data is added
+* CLJS-3438: Inference for `goog.object/containsKey` returns any, not boolean
+
## 1.12.42
### Changes
@@ -79,7 +117,7 @@
* CLJS-3372: Vendorize data.json, transit-clj, and tools.reader
data.json and transit-clj are no longer dependencies. CLJS-3375 bridges
tools.reader for backwards compatibility
-* Clojure 1.10 minimum version
+* Clojure 1.10 minimum version
* Update Google Closure Compiler, transit-java, tools.reader dependencies to latest
* CLJS-2820 Compile cljs.loader regardless of whether :modules are used
* CLJS-3370: improved uuid regex to only accept hex characters
@@ -580,7 +618,7 @@
* cljs.main, simple command line access to Compiler & REPLs
* cljs.server.* namespaces for integration with -Dclojure.server.repl
* :aot-cache compiler to enable global AOT caching of dependencies in JARs
-* :stable-names compiler flag, to support vendorization when using :modules,
+* :stable-names compiler flag, to support vendorization when using :modules,
defaults to true when using :modules.
* Add :webworker & :nashorn target
* pREPL implementation (usage requires Clojure 1.10.0-alpha)
@@ -859,7 +897,7 @@
### Fixes
* CLJS-2139: Undeclared var regression in fn bodies
-* CLJS-2137: Missing INext on some sequences
+* CLJS-2137: Missing INext on some sequences
* CLJS-2136: Clarify IFind contract to avoid double-lookups
* need to elide :c.a/analyzed in c.a/analyze-wrap-meta to avoid dumping unintended
with-meta expressions
@@ -958,12 +996,12 @@
### Changes
* CLJS-2021: subvec throws when passed non-vector
-* CLJS-1884: Give a chance to MetaFn to be removed by closure under :advanced
+* CLJS-1884: Give a chance to MetaFn to be removed by closure under :advanced
optimization Replace with-meta calls by -with-meta calls where possible
* CLJS-2052: Port new spec.alpha enhancements
* Update Google Closure Compiler dependency
* Update Google Closure Library dependency
-
+
### Fixes
* CLJS-2053: Regression: cljs.spec.alpha/any for fdef
* CLJS-2039: remove extraneous argument from ChunkBuffer.chunk
@@ -1011,7 +1049,7 @@
### Changes
* CLJS-2006: Upgrade Closure Compiler to April 2017 release
-### Fixes
+### Fixes
* CLJS-1497: `find` on an associative collection does not return collection key
* CLJS-1996: Support correct checking of :preloads when :optimizations not specified
* CLJS-1994: assoc on nil returns PHM (expected PAM)
@@ -1340,7 +1378,7 @@ possible
* CLJS-1661: cljs.spec: non-spec'ed fn var printing
* compute read/write opts for transit if possible, handle JSValue
* CLJS-1660: cljs.spec: Always return var from instrument / unstrument
-* CLJS-1671: Bad cljs.spec interactive instrumentation session
+* CLJS-1671: Bad cljs.spec interactive instrumentation session
* CLJS-1664: The filename aux.cljs is a problem on windows.
* CLJS-1667: bad describe* for and-spec-impl
* CLJS-1699: Self-host: s/fdef ns-qualify *ns* name field access
@@ -1715,7 +1753,7 @@ determine which version you should use.
* CLJS-1203: standard way to pass multiple directories to build
### Fixes
-* CLJS-1216: incorrect max fixed arity for fns both multi-arity and variadic
+* CLJS-1216: incorrect max fixed arity for fns both multi-arity and variadic
* cljs.analyzer/parse-ns did not bind *cljs-file*
* CLJS-1201: compare broken for IIndexed collections
* CLJS-1202: cljs.repl/load-file is not additive
diff --git a/deps.edn b/deps.edn
index 012e220693..b7057d93f0 100644
--- a/deps.edn
+++ b/deps.edn
@@ -1,6 +1,6 @@
{:paths ["src/main/clojure" "src/main/cljs" "resources"]
:deps
- {com.google.javascript/closure-compiler {:mvn/version "v20250402"}
+ {com.google.javascript/closure-compiler {:mvn/version "v20250820"}
com.cognitect/transit-java {:mvn/version "1.0.362"}
org.clojure/clojure {:mvn/version "1.10.0"}
org.clojure/core.specs.alpha {:mvn/version "0.1.24"}
@@ -9,7 +9,13 @@
org.clojure/tools.reader {:mvn/version "1.3.6"}
org.clojure/test.check {:mvn/version "1.1.1"}}
:aliases
- {:cli.test.run {:extra-paths ["src/test/cljs_cli"]
+ {:cljs-repl {:extra-paths ["src/test/cljs"]
+ :main-opts ["-m" "cljs.main" "-re" "node" "-d" ".cljs_repl" "-r"]}
+ :cljs-brepl {:extra-paths ["src/test/cljs"]
+ :main-opts ["-m" "cljs.main" "-d" ".cljs_brepl" "-r"]}
+ :cljs-lite-repl {:extra-paths ["src/test/cljs"]
+ :main-opts ["-m" "cljs.main" "-co" "{:lite-mode true}" "-re" "node" "-d" ".cljs_lite_repl" "-r"]}
+ :cli.test.run {:extra-paths ["src/test/cljs_cli"]
:main-opts ["-i" "src/test/cljs_cli/cljs_cli/test_runner.clj"
"-e" "(cljs-cli.test-runner/-main)"]}
:compiler.test {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp"
@@ -19,6 +25,8 @@
"-e" "(cljs.test-runner/-main)"]}
:runtime.test.build {:extra-paths ["src/test/cljs"]
:main-opts ["-m" "cljs.main" "-co" "resources/test.edn" "-c"]}
+ :lite.test.build {:extra-paths ["src/test/cljs"]
+ :main-opts ["-m" "cljs.main" "-co" "resources/lite_test.edn" "-c"]}
:selfhost.test.build {:extra-paths ["src/test/self"]
:main-opts ["-m" "cljs.main" "-co" "resources/self_host_test.edn" "-c"]}
:selfparity.test.build {:extra-paths ["src/test/self"]
diff --git a/pom.template.xml b/pom.template.xml
index 884a2d6280..f6a75f4839 100644
--- a/pom.template.xml
+++ b/pom.template.xml
@@ -30,7 +30,7 @@
com.google.javascript
closure-compiler
- v20250402
+ v20250820
org.clojure
@@ -409,7 +409,7 @@
org.sonatype.central
central-publishing-maven-plugin
- 0.7.0
+ 0.9.0
true
central
diff --git a/project.clj b/project.clj
index 3977529e51..0bba1a4896 100644
--- a/project.clj
+++ b/project.clj
@@ -15,7 +15,7 @@
[org.clojure/test.check "1.1.1" :scope "test"]
[com.cognitect/transit-java "1.0.362"]
[org.clojure/google-closure-library "0.0-20250515-f04e4c0e"]
- [com.google.javascript/closure-compiler "v20250402"]]
+ [com.google.javascript/closure-compiler "v20250820"]]
:profiles {:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]}
:uberjar {:aot :all :main cljs.main}
:closure-snapshot {:dependencies [[com.google.javascript/closure-compiler-unshaded "1.0-SNAPSHOT"]]}}
diff --git a/resources/lite_test.edn b/resources/lite_test.edn
new file mode 100644
index 0000000000..44508575d6
--- /dev/null
+++ b/resources/lite_test.edn
@@ -0,0 +1,28 @@
+{:optimizations :advanced
+ :main lite-test-runner
+ :output-to "builds/out-lite/lite-test.js"
+ :output-dir "builds/out-lite"
+ :output-wrapper true
+ :verbose true
+ :compiler-stats true
+ :parallel-build true
+ :npm-deps {:lodash "4.17.4"}
+ :closure-warnings {:non-standard-jsdoc :off :global-this :off}
+ :install-deps true
+ :language-out :es5
+ :foreign-libs
+ [{:file "src/test/cljs/calculator_global.js"
+ :provides ["calculator"]
+ :global-exports {calculator Calculator}}
+ {:file "src/test/cljs/es6_dep.js"
+ :module-type :es6
+ :provides ["es6_calc"]}
+ {:file "src/test/cljs/calculator.js"
+ :module-type :commonjs
+ :provides ["calculator"]}
+ {:file "src/test/cljs/es6_default_hello.js"
+ :provides ["es6_default_hello"]
+ :module-type :es6}]
+ :pseudo-names true
+ :pretty-print true
+ :lite-mode true}
diff --git a/resources/test.edn b/resources/test.edn
index a8f2581390..a9b40633e5 100644
--- a/resources/test.edn
+++ b/resources/test.edn
@@ -9,7 +9,7 @@
:npm-deps {:lodash "4.17.4"}
:closure-warnings {:non-standard-jsdoc :off :global-this :off}
:install-deps true
- :language-out :es5
+ :language-out :es6
:foreign-libs
[{:file "src/test/cljs/calculator_global.js"
:provides ["calculator"]
diff --git a/script/bootstrap b/script/bootstrap
index 6b2a6e44ba..464cc08da6 100755
--- a/script/bootstrap
+++ b/script/bootstrap
@@ -5,7 +5,7 @@ set -e
CLOJURE_RELEASE="1.9.0"
SPEC_ALPHA_RELEASE="0.1.143"
CORE_SPECS_ALPHA_RELEASE="0.1.24"
-CLOSURE_RELEASE="20250402"
+CLOSURE_RELEASE="20250820"
GCLOSURE_LIB_RELEASE="0.0-20250515-f04e4c0e"
TREADER_RELEASE="1.3.6"
TEST_CHECK_RELEASE="1.1.1"
diff --git a/src/main/cljs/cljs/analyzer/passes/lite.cljc b/src/main/cljs/cljs/analyzer/passes/lite.cljc
new file mode 100644
index 0000000000..d0ea8c659b
--- /dev/null
+++ b/src/main/cljs/cljs/analyzer/passes/lite.cljc
@@ -0,0 +1,32 @@
+;; 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 cljs.analyzer.passes.lite
+ (:refer-clojure :exclude [var?]))
+
+(defn var? [ast]
+ (= :var (:op ast)))
+
+(def ctor->ctor-lite
+ '{cljs.core/vector cljs.core/vector-lite
+ cljs.core/vec cljs.core/vec-lite})
+
+(defn update-var [{:keys [name] :as ast}]
+ (let [replacement (get ctor->ctor-lite name)]
+ (-> ast
+ (assoc :name replacement)
+ (assoc-in [:info :name] replacement))))
+
+(defn replace-var? [ast]
+ (and (var? ast)
+ (contains? ctor->ctor-lite (:name ast))))
+
+(defn use-lite-types
+ [env ast _]
+ (cond-> ast
+ (replace-var? ast) update-var))
diff --git a/src/main/cljs/cljs/core.cljs b/src/main/cljs/cljs/core.cljs
index 3e789b6dcf..c5d1866893 100644
--- a/src/main/cljs/cljs/core.cljs
+++ b/src/main/cljs/cljs/core.cljs
@@ -53,6 +53,10 @@
, and \"global\" supported. "}
*global* "default")
+(goog-define
+ ^{:doc "Boolean flag for LITE_MODE"}
+ LITE_MODE false)
+
(def
^{:dynamic true
:doc "Var bound to the current namespace. Only used for bootstrapping."
@@ -243,7 +247,7 @@
[x]
(coercive-= x nil))
-(defn ^boolean array?
+(defn array?
"Returns true if x is a JavaScript array."
[x]
(if (identical? *target* "nodejs")
@@ -267,6 +271,31 @@
"Returns true if x is not nil, false otherwise."
[x] (not (nil? x)))
+(defn- pr-opts-fnl [opts]
+ (if-not (nil? opts)
+ (:flush-on-newline opts)
+ *flush-on-newline*))
+
+(defn- pr-opts-readably [opts]
+ (if-not (nil? opts)
+ (:readably opts)
+ *print-readably*))
+
+(defn- pr-opts-meta [opts]
+ (if-not (nil? opts)
+ (:meta opts)
+ *print-meta*))
+
+(defn- pr-opts-dup [opts]
+ (if-not (nil? opts)
+ (:dup opts)
+ *print-dup*))
+
+(defn- pr-opts-len [opts]
+ (if-not (nil? opts)
+ (:print-length opts)
+ *print-length*))
+
(defn object?
"Returns true if x's constructor is Object"
[x]
@@ -332,7 +361,7 @@
(defn type->str [ty]
(if-let [s (.-cljs$lang$ctorStr ty)]
s
- (str ty)))
+ (str_ ty)))
;; INTERNAL - do not use, only for Node.js
(defn load-file [file]
@@ -419,7 +448,7 @@
(declare apply)
-(defn ^array make-array
+(defn make-array
"Construct a JavaScript array of the specified dimensions. Accepts ignored
type argument for compatibility with Clojure. Note that there is no efficient
way to allocate multi-dimensional arrays in JavaScript; as such, this function
@@ -907,9 +936,9 @@
[^not-native obj]
(let [sb (StringBuffer.)
writer (StringBufferWriter. sb)]
- (-pr-writer obj writer (pr-opts))
+ (-pr-writer obj writer nil)
(-flush writer)
- (str sb)))
+ (.toString sb)))
;;;;;;;;;;;;;;;;;;; Murmur3 ;;;;;;;;;;;;;;;
@@ -1011,7 +1040,7 @@
h))
(defn hash-string [k]
- (when (> string-hash-cache-count 255)
+ (when (> string-hash-cache-count 1024)
(set! string-hash-cache (js-obj))
(set! string-hash-cache-count 0))
(if (nil? k)
@@ -1030,8 +1059,8 @@
(bit-xor (-hash o) 0)
(number? o)
- (if ^boolean (js/isFinite o)
- (if-not ^boolean (.isSafeInteger js/Number o)
+ (if (js/isFinite o)
+ (if-not (.isSafeInteger js/Number o)
(hash-double o)
(js-mod (Math/floor o) 2147483647))
(case o
@@ -1150,7 +1179,7 @@
:else (throw (new js/Error "no conversion to symbol"))))
([ns name]
(let [sym-str (if-not (nil? ns)
- (str ns "/" name)
+ (str_ ns "/" name)
name)]
(Symbol. ns name sym-str nil nil))))
@@ -1159,7 +1188,7 @@
(isMacro [_]
(. (val) -cljs$lang$macro))
(toString [_]
- (str "#'" sym))
+ (str_ "#'" sym))
IDeref
(-deref [_] (val))
IMeta
@@ -1274,7 +1303,7 @@
(native-satisfies? ISeqable coll)
(-seq coll)
- :else (throw (js/Error. (str coll " is not ISeqable"))))))
+ :else (throw (js/Error. (str_ coll " is not ISeqable"))))))
(defn first
"Returns the first item in the collection. Calls seq on its
@@ -1423,7 +1452,7 @@
(-compare [this other]
(if (instance? js/Date other)
(garray/defaultCompare (.valueOf this) (.valueOf other))
- (throw (js/Error. (str "Cannot compare " this " to " other))))))
+ (throw (js/Error. (str_ "Cannot compare " this " to " other))))))
(defprotocol Inst
(inst-ms* [inst]))
@@ -1453,10 +1482,18 @@
IMeta
(-meta [_] nil))
+(defn- root-obj
+ []
+ (->> js/Function
+ (.getPrototypeOf js/Object)
+ (.getPrototypeOf js/Object)))
+
(extend-type default
IHash
(-hash [o]
- (goog/getUid o)))
+ (if (identical? o (root-obj))
+ 0
+ (goog/getUid o))))
(extend-type symbol
IHash
@@ -1578,7 +1615,7 @@ reduces them without incurring seq initialization"
-1
(loop [idx (cond
(pos? start) start
- (neg? start) (max 0 (+ start len))
+ (neg? start) (unchecked-max 0 (+ start len))
:else start)]
(if (< idx len)
(if (= (nth coll idx) x)
@@ -1594,7 +1631,7 @@ reduces them without incurring seq initialization"
(if (zero? len)
-1
(loop [idx (cond
- (pos? start) (min (dec len) start)
+ (pos? start) (unchecked-min (dec len) start)
(neg? start) (+ len start)
:else start)]
(if (>= idx 0)
@@ -1648,7 +1685,7 @@ reduces them without incurring seq initialization"
(-first [_] (aget arr i))
(-rest [_] (if (< (inc i) (alength arr))
(IndexedSeq. arr (inc i) nil)
- (list)))
+ ()))
INext
(-next [_] (if (< (inc i) (alength arr))
@@ -1665,7 +1702,7 @@ reduces them without incurring seq initialization"
ICounted
(-count [_]
- (max 0 (- (alength arr) i)))
+ (unchecked-max 0 (- (alength arr) i)))
IIndexed
(-nth [coll n]
@@ -1738,7 +1775,7 @@ reduces them without incurring seq initialization"
(indexOf [coll x start]
(-indexOf coll x start))
(lastIndexOf [coll x]
- (-lastIndexOf coll x (count coll)))
+ (-lastIndexOf coll x (-count coll)))
(lastIndexOf [coll x start]
(-lastIndexOf coll x start))
@@ -1942,7 +1979,7 @@ reduces them without incurring seq initialization"
(-nth coll n)
:else
- (throw (js/Error. (str "nth not supported on this type "
+ (throw (js/Error. (str_ "nth not supported on this type "
(type->str (type coll)))))))
([coll n not-found]
(cond
@@ -1975,7 +2012,7 @@ reduces them without incurring seq initialization"
(-nth coll n not-found)
:else
- (throw (js/Error. (str "nth not supported on this type "
+ (throw (js/Error. (str_ "nth not supported on this type "
(type->str (type coll))))))))
(defn nthrest
@@ -2045,7 +2082,7 @@ reduces them without incurring seq initialization"
(-assoc coll k v)
(if-not (nil? coll)
(-assoc coll k v)
- (array-map k v))))
+ {k v})))
([coll k v & kvs]
(let [ret (assoc coll k v)]
(if kvs
@@ -2237,7 +2274,10 @@ reduces them without incurring seq initialization"
(defn chunked-seq?
"Return true if x satisfies IChunkedSeq."
- [x] (implements? IChunkedSeq x))
+ [x]
+ (if-not ^boolean LITE_MODE
+ (implements? IChunkedSeq x)
+ false))
;;;;;;;;;;;;;;;;;;;; js primitives ;;;;;;;;;;;;
(defn js-obj
@@ -2330,7 +2370,7 @@ reduces them without incurring seq initialization"
"Returns true if n is a JavaScript number with no decimal part."
[n]
(and (number? n)
- (not ^boolean (js/isNaN n))
+ (not (js/isNaN n))
(not (identical? n js/Infinity))
(== (js/parseFloat n) (js/parseInt n 10))))
@@ -2407,7 +2447,7 @@ reduces them without incurring seq initialization"
(or (identical? x js/Number.POSITIVE_INFINITY)
(identical? x js/Number.NEGATIVE_INFINITY)))
-(defn contains?
+(defn ^boolean contains?
"Returns true if key is present in the given collection, otherwise
returns false. Note that for numerically indexed collections like
vectors and arrays, this tests if the numeric key is within the
@@ -2437,12 +2477,12 @@ reduces them without incurring seq initialization"
(contains? coll k))
(MapEntry. k (get coll k) nil))))
-(defn ^boolean distinct?
+(defn distinct?
"Returns true if no two of the arguments are ="
([x] true)
([x y] (not (= x y)))
([x y & more]
- (if (not (= x y))
+ (if (not (= x y))
(loop [s #{x y} xs more]
(let [x (first xs)
etc (next xs)]
@@ -2470,7 +2510,7 @@ reduces them without incurring seq initialization"
(number? x) (if (number? y)
(garray/defaultCompare x y)
- (throw (js/Error. (str "Cannot compare " x " to " y))))
+ (throw (js/Error. (str_ "Cannot compare " x " to " y))))
(satisfies? IComparable x)
(-compare x y)
@@ -2479,7 +2519,7 @@ reduces them without incurring seq initialization"
(if (and (or (string? x) (array? x) (true? x) (false? x))
(identical? (type x) (type y)))
(garray/defaultCompare x y)
- (throw (js/Error. (str "Cannot compare " x " to " y))))))
+ (throw (js/Error. (str_ "Cannot compare " x " to " y))))))
(defn ^:private compare-indexed
"Compare indexed collection."
@@ -2768,17 +2808,32 @@ reduces them without incurring seq initialization"
:added "1.11.10"}
[a] (Math/abs a))
+(defn NaN?
+ "Returns true if num is NaN, else false"
+ [val]
+ (js/isNaN val))
+
(defn ^number max
"Returns the greatest of the nums."
([x] x)
- ([x y] (cljs.core/max x y))
+ ([x y]
+ (cond
+ (NaN? x) x
+ (NaN? y) y
+ (> x y) x
+ :else y))
([x y & more]
(reduce max (cljs.core/max x y) more)))
(defn ^number min
"Returns the least of the nums."
([x] x)
- ([x y] (cljs.core/min x y))
+ ([x y]
+ (cond
+ (NaN? x) x
+ (NaN? y) y
+ (< x y) x
+ :else y))
([x y & more]
(reduce min (cljs.core/min x y) more)))
@@ -2884,22 +2939,22 @@ reduces them without incurring seq initialization"
(Math/ceil q)))
(defn int
- "Coerce to int by stripping decimal places."
+ "Coerce to int."
[x]
(bit-or x 0))
(defn unchecked-int
- "Coerce to int by stripping decimal places."
+ "Coerce to int."
[x]
(fix x))
(defn long
- "Coerce to long by stripping decimal places. Identical to `int'."
+ "Coerce to long. Identical to `int'."
[x]
(fix x))
(defn unchecked-long
- "Coerce to long by stripping decimal places. Identical to `int'."
+ "Coerce to long. Identical to `int'."
[x]
(fix x))
@@ -3047,6 +3102,29 @@ reduces them without incurring seq initialization"
;;;;;;;;;;;;;;;;;;;;;;;;;; basics ;;;;;;;;;;;;;;;;;;
+(defn- str_
+ "Implementation detail. Internal str without circularity on IndexedSeq.
+ @param x
+ @param {...*} var_args"
+ [x var-args]
+ (cond
+ ;; works whether x is undefined or null (cljs nil)
+ (nil? x) ""
+ ;; if we have no more parameters, return
+ (undefined? var-args) (.join #js [x] "")
+ ;; var arg case without relying on CLJS fn machinery which creates
+ ;; a circularity via IndexedSeq
+ :else
+ (let [sb (StringBuffer.)
+ args (js-arguments)
+ len (alength args)]
+ (loop [i 0]
+ (if (< i len)
+ (do
+ (.append sb (cljs.core/str_ (aget args i)))
+ (recur (inc i)))
+ (.toString sb))))))
+
(defn str
"With no args, returns the empty string. With one arg x, returns
x.toString(). (str nil) returns the empty string. With more than
@@ -3054,12 +3132,12 @@ reduces them without incurring seq initialization"
([] "")
([x] (if (nil? x)
""
- (.join #js [x] "")))
+ (.toString x)))
([x & ys]
- (loop [sb (StringBuffer. (str x)) more ys]
- (if more
- (recur (. sb (append (str (first more)))) (next more))
- (.toString sb)))))
+ (loop [sb (StringBuffer. (str x)) more ys]
+ (if more
+ (recur (. sb (append (str (first more)))) (next more))
+ (.toString sb)))))
(defn subs
"Returns the substring of s beginning at start inclusive, and ending
@@ -3206,8 +3284,7 @@ reduces them without incurring seq initialization"
(deftype EmptyList [meta]
Object
- (toString [coll]
- (pr-str* coll))
+ (toString [coll] "()")
(equiv [this other]
(-equiv this other))
(indexOf [coll x]
@@ -3395,7 +3472,7 @@ reduces them without incurring seq initialization"
(deftype Keyword [ns name fqn ^:mutable _hash]
Object
- (toString [_] (str ":" fqn))
+ (toString [_] (str_ ":" fqn))
(equiv [this other]
(-equiv this other))
@@ -3419,7 +3496,7 @@ reduces them without incurring seq initialization"
(-namespace [_] ns)
IPrintWithWriter
- (-pr-writer [o writer _] (-write writer (str ":" fqn))))
+ (-pr-writer [o writer _] (-write writer (str_ ":" fqn))))
(defn keyword?
"Return true if x is a Keyword"
@@ -3449,7 +3526,7 @@ reduces them without incurring seq initialization"
[x]
(if (implements? INamed x)
(-namespace x)
- (throw (js/Error. (str "Doesn't support namespace: " x)))))
+ (throw (js/Error. (str_ "Doesn't support namespace: " x)))))
(defn ident?
"Return true if x is a symbol or keyword"
@@ -3501,7 +3578,7 @@ reduces them without incurring seq initialization"
(keyword? name) (cljs.core/name name)
(symbol? name) (cljs.core/name name)
:else name)]
- (Keyword. ns name (str (when ns (str ns "/")) name) nil))))
+ (Keyword. ns name (str_ (when ns (str_ ns "/")) name) nil))))
(deftype LazySeq [meta ^:mutable fn ^:mutable s ^:mutable __hash]
Object
@@ -3559,7 +3636,13 @@ reduces them without incurring seq initialization"
(-conj [coll o] (cons o coll))
IEmptyableCollection
- (-empty [coll] (-with-meta (.-EMPTY List) meta))
+ (-empty [coll]
+ ;; MAYBE FIXME: :lite-mode testing uncovered a very old bug, empty on seq
+ ;; should discard the metadata, we changed the behavior in LITE_MODE for now
+ ;; to avoid a breaking change
+ (if-not ^boolean LITE_MODE
+ (-with-meta (.-EMPTY List) meta)
+ (.-EMPTY List)))
ISequential
IEquiv
@@ -4063,16 +4146,26 @@ reduces them without incurring seq initialization"
(set! *unchecked-if* false)
+(declare ObjMap)
+
;; CLJS-3200: used by destructure macro for maps to reduce amount of repeated code
;; placed here because it needs apply and hash-map (only declared at this point)
(defn --destructure-map [gmap]
- (if (implements? ISeq gmap)
- (if (next gmap)
- (.createAsIfByAssoc PersistentArrayMap (to-array gmap))
- (if (seq gmap)
- (first gmap)
- (.-EMPTY PersistentArrayMap)))
- gmap))
+ (if ^boolean LITE_MODE
+ (if (implements? ISeq gmap)
+ (if (next gmap)
+ (.createAsIfByAssoc ObjMap (to-array gmap))
+ (if (seq gmap)
+ (first gmap)
+ (.-EMPTY ObjMap)))
+ gmap)
+ (if (implements? ISeq gmap)
+ (if (next gmap)
+ (.createAsIfByAssoc PersistentArrayMap (to-array gmap))
+ (if (seq gmap)
+ (first gmap)
+ (.-EMPTY PersistentArrayMap)))
+ gmap)))
(defn vary-meta
"Returns an object of the same type and value as obj, with
@@ -4163,7 +4256,7 @@ reduces them without incurring seq initialization"
(string? coll) (string-iter coll)
(array? coll) (array-iter coll)
(seqable? coll) (seq-iter coll)
- :else (throw (js/Error. (str "Cannot create iterator from " coll)))))
+ :else (throw (js/Error. (str_ "Cannot create iterator from " coll)))))
(deftype Many [vals]
Object
@@ -4175,7 +4268,7 @@ reduces them without incurring seq initialization"
(isEmpty [this]
(zero? (.-length vals)))
(toString [this]
- (str "Many: " vals)))
+ (str_ "Many: " vals)))
(def ^:private NONE #js {})
@@ -4189,21 +4282,21 @@ reduces them without incurring seq initialization"
(Many. #js [val o])))
(remove [this]
(if (identical? val NONE)
- (throw (js/Error. (str "Removing object from empty buffer")))
+ (throw (js/Error. (str_ "Removing object from empty buffer")))
(let [ret val]
(set! val NONE)
ret)))
(isEmpty [this]
(identical? val NONE))
(toString [this]
- (str "Single: " val)))
+ (str_ "Single: " val)))
(deftype Empty []
Object
(add [this o]
(Single. o))
(remove [this]
- (throw (js/Error. (str "Removing object from empty buffer"))))
+ (throw (js/Error. (str_ "Removing object from empty buffer"))))
(isEmpty [this]
true)
(toString [this]
@@ -4350,8 +4443,8 @@ reduces them without incurring seq initialization"
(defn even?
"Returns true if n is even, throws an exception if n is not an integer"
[n] (if (integer? n)
- (zero? (bit-and n 1))
- (throw (js/Error. (str "Argument must be an integer: " n)))))
+ (zero? (bit-and n 1))
+ (throw (js/Error. (str_ "Argument must be an integer: " n)))))
(defn odd?
"Returns true if n is odd, throws an exception if n is not an integer"
@@ -5525,7 +5618,7 @@ reduces them without incurring seq initialization"
ret))))))
(defn- vector-index-out-of-bounds [i cnt]
- (throw (js/Error. (str "No item " i " in vector of length " cnt))))
+ (throw (js/Error. (str_ "No item " i " in vector of length " cnt))))
(defn- first-array-for-longvec [pv]
;; invariants: (count pv) > 32.
@@ -5754,14 +5847,14 @@ reduces them without incurring seq initialization"
IVector
(-assoc-n [coll n val]
(cond
- (and (<= 0 n) (< n cnt))
- (if (<= (tail-off coll) n)
+ (and (<= 0 n) (< n cnt))
+ (if (<= (tail-off coll) n)
(let [new-tail (aclone tail)]
(aset new-tail (bit-and n 0x01f) val)
(PersistentVector. meta cnt shift root new-tail nil))
(PersistentVector. meta cnt shift (do-assoc coll shift root n val) tail nil))
- (== n cnt) (-conj coll val)
- :else (throw (js/Error. (str "Index " n " out of bounds [0," cnt "]")))))
+ (== n cnt) (-conj coll val)
+ :else (throw (js/Error. (str_ "Index " n " out of bounds [0," cnt "]")))))
IReduce
(-reduce [v f]
@@ -5941,6 +6034,10 @@ reduces them without incurring seq initialization"
(-empty [coll]
())
+ ICounted
+ (-count [coll]
+ (- (-count vec) (+ i off)))
+
IChunkedSeq
(-chunked-first [coll]
(array-chunk node off))
@@ -6080,8 +6177,8 @@ reduces them without incurring seq initialization"
(-assoc-n [coll n val]
(let [v-pos (+ start n)]
(if (or (neg? n) (<= (inc end) v-pos))
- (throw (js/Error. (str "Index " n " out of bounds [0," (-count coll) "]")))
- (build-subvec meta (assoc v v-pos val) start (max end (inc v-pos)) nil))))
+ (throw (js/Error. (str_ "Index " n " out of bounds [0," (-count coll) "]")))
+ (build-subvec meta (assoc v v-pos val) start (unchecked-max end (inc v-pos)) nil))))
IReduce
(-reduce [coll f]
@@ -6268,7 +6365,7 @@ reduces them without incurring seq initialization"
:else
(throw
(js/Error.
- (str "Index " n " out of bounds for TransientVector of length" cnt))))
+ (str_ "Index " n " out of bounds for TransientVector of length" cnt))))
(throw (js/Error. "assoc! after persistent!"))))
(-pop! [tcoll]
@@ -6473,7 +6570,7 @@ reduces them without incurring seq initialization"
ICounted
(-count [coll] count))
-(set! (.-EMPTY PersistentQueue) (PersistentQueue. nil 0 nil [] empty-ordered-hash))
+(set! (.-EMPTY PersistentQueue) (PersistentQueue. nil 0 nil (.-EMPTY PersistentVector) empty-ordered-hash))
(es6-iterable PersistentQueue)
@@ -6505,172 +6602,6 @@ reduces them without incurring seq initialization"
(= (get y (first xkv) never-equiv) (second xkv)))
x))))))
-
-(defn- scan-array [incr k array]
- (let [len (alength array)]
- (loop [i 0]
- (when (< i len)
- (if (identical? k (aget array i))
- i
- (recur (+ i incr)))))))
-
-; The keys field is an array of all keys of this map, in no particular
-; order. Any string, keyword, or symbol key is used as a property name
-; to store the value in strobj. If a key is assoc'ed when that same
-; key already exists in strobj, the old value is overwritten. If a
-; non-string key is assoc'ed, return a HashMap object instead.
-
-(defn- obj-map-compare-keys [a b]
- (let [a (hash a)
- b (hash b)]
- (cond
- (< a b) -1
- (> a b) 1
- :else 0)))
-
-(defn- obj-map->hash-map [m k v]
- (let [ks (.-keys m)
- len (alength ks)
- so (.-strobj m)
- mm (meta m)]
- (loop [i 0
- out (transient (.-EMPTY PersistentHashMap))]
- (if (< i len)
- (let [k (aget ks i)]
- (recur (inc i) (assoc! out k (gobject/get so k))))
- (-with-meta (persistent! (assoc! out k v)) mm)))))
-
-;;; ObjMap - DEPRECATED
-
-(defn- obj-clone [obj ks]
- (let [new-obj (js-obj)
- l (alength ks)]
- (loop [i 0]
- (when (< i l)
- (let [k (aget ks i)]
- (gobject/set new-obj k (gobject/get obj k))
- (recur (inc i)))))
- new-obj))
-
-(deftype ObjMap [meta keys strobj update-count ^:mutable __hash]
- Object
- (toString [coll]
- (pr-str* coll))
- (equiv [this other]
- (-equiv this other))
-
- IWithMeta
- (-with-meta [coll new-meta]
- (if (identical? new-meta meta)
- coll
- (ObjMap. new-meta keys strobj update-count __hash)))
-
- IMeta
- (-meta [coll] meta)
-
- ICollection
- (-conj [coll entry]
- (if (vector? entry)
- (-assoc coll (-nth entry 0) (-nth entry 1))
- (reduce -conj
- coll
- entry)))
-
- IEmptyableCollection
- (-empty [coll] (-with-meta (.-EMPTY ObjMap) meta))
-
- IEquiv
- (-equiv [coll other] (equiv-map coll other))
-
- IHash
- (-hash [coll] (caching-hash coll hash-unordered-coll __hash))
-
- ISeqable
- (-seq [coll]
- (when (pos? (alength keys))
- (map #(vector % (unchecked-get strobj %))
- (.sort keys obj-map-compare-keys))))
-
- ICounted
- (-count [coll] (alength keys))
-
- ILookup
- (-lookup [coll k] (-lookup coll k nil))
- (-lookup [coll k not-found]
- (if (and (string? k)
- (not (nil? (scan-array 1 k keys))))
- (unchecked-get strobj k)
- not-found))
-
- IAssociative
- (-assoc [coll k v]
- (if (string? k)
- (if (or (> update-count (.-HASHMAP_THRESHOLD ObjMap))
- (>= (alength keys) (.-HASHMAP_THRESHOLD ObjMap)))
- (obj-map->hash-map coll k v)
- (if-not (nil? (scan-array 1 k keys))
- (let [new-strobj (obj-clone strobj keys)]
- (gobject/set new-strobj k v)
- (ObjMap. meta keys new-strobj (inc update-count) nil)) ; overwrite
- (let [new-strobj (obj-clone strobj keys) ; append
- new-keys (aclone keys)]
- (gobject/set new-strobj k v)
- (.push new-keys k)
- (ObjMap. meta new-keys new-strobj (inc update-count) nil))))
- ;; non-string key. game over.
- (obj-map->hash-map coll k v)))
- (-contains-key? [coll k]
- (if (and (string? k)
- (not (nil? (scan-array 1 k keys))))
- true
- false))
-
- IFind
- (-find [coll k]
- (when (and (string? k)
- (not (nil? (scan-array 1 k keys))))
- (MapEntry. k (unchecked-get strobj k) nil)))
-
- IKVReduce
- (-kv-reduce [coll f init]
- (let [len (alength keys)]
- (loop [keys (.sort keys obj-map-compare-keys)
- init init]
- (if (seq keys)
- (let [k (first keys)
- init (f init k (unchecked-get strobj k))]
- (if (reduced? init)
- @init
- (recur (rest keys) init)))
- init))))
-
- IMap
- (-dissoc [coll k]
- (if (and (string? k)
- (not (nil? (scan-array 1 k keys))))
- (let [new-keys (aclone keys)
- new-strobj (obj-clone strobj keys)]
- (.splice new-keys (scan-array 1 k new-keys) 1)
- (js-delete new-strobj k)
- (ObjMap. meta new-keys new-strobj (inc update-count) nil))
- coll)) ; key not found, return coll unchanged
-
- IFn
- (-invoke [coll k]
- (-lookup coll k))
- (-invoke [coll k not-found]
- (-lookup coll k not-found))
-
- IEditableCollection
- (-as-transient [coll]
- (transient (into (hash-map) coll))))
-
-(set! (.-EMPTY ObjMap) (ObjMap. nil (array) (js-obj) 0 empty-unordered-hash))
-
-(set! (.-HASHMAP_THRESHOLD ObjMap) 8)
-
-(set! (.-fromObject ObjMap) (fn [ks obj] (ObjMap. nil ks obj 0 nil)))
-
;; Record Iterator
(deftype RecordIter [^:mutable i record base-count fields ext-map-iter]
Object
@@ -6838,14 +6769,16 @@ reduces them without incurring seq initialization"
IIndexed
(-nth [node n]
- (cond (== n 0) key
- (== n 1) val
- :else (throw (js/Error. "Index out of bounds"))))
+ (case n
+ 0 key
+ 1 val
+ (throw (js/Error. "Index out of bounds"))))
(-nth [node n not-found]
- (cond (== n 0) key
- (== n 1) val
- :else not-found))
+ (case n
+ 0 key
+ 1 val
+ not-found))
ILookup
(-lookup [node k] (-nth node k nil))
@@ -6855,7 +6788,10 @@ reduces them without incurring seq initialization"
(-assoc [node k v]
(assoc [key val] k v))
(-contains-key? [node k]
- (or (== k 0) (== k 1)))
+ (case k
+ 0 true
+ 1 true
+ false))
IFind
(-find [node k]
@@ -7175,7 +7111,7 @@ reduces them without incurring seq initialization"
idx (array-index-of ret k)]
(if (== idx -1)
(doto ret (.push k) (.push v))
- (throw (js/Error. (str "Duplicate key: " k)))))
+ (throw (js/Error. (str_ "Duplicate key: " k)))))
(recur (+ i 2))))
(let [cnt (/ (alength arr) 2)]
(PersistentArrayMap. nil cnt arr nil)))))
@@ -7227,7 +7163,7 @@ reduces them without incurring seq initialization"
(fn [init]
;; check trailing element
(let [len (alength init)
- has-trailing? (== 1 (bit-and len 1))]
+ has-trailing? (== 1 (bit-and len 1))]
(if-not (or has-trailing? (pam-dupes? init))
(PersistentArrayMap. nil (/ len 2) init nil)
(.createAsIfByAssocComplexPath PersistentArrayMap init has-trailing?)))))
@@ -8244,7 +8180,7 @@ reduces them without incurring seq initialization"
(loop [i 0 ^not-native out (transient (.-EMPTY PersistentHashMap))]
(if (< i len)
(if (<= (alength vs) i)
- (throw (js/Error. (str "No value supplied for key: " (aget ks i))))
+ (throw (js/Error. (str_ "No value supplied for key: " (aget ks i))))
(recur (inc i) (-assoc! out (aget ks i) (aget vs i))))
(persistent! out))))))
@@ -8256,7 +8192,7 @@ reduces them without incurring seq initialization"
(when (< i len)
(-assoc! ret (aget arr i) (aget arr (inc i)))
(if (not= (-count ret) (inc (/ i 2)))
- (throw (js/Error. (str "Duplicate key: " (aget arr i))))
+ (throw (js/Error. (str_ "Duplicate key: " (aget arr i))))
(recur (+ i 2)))))
(-persistent! ret))))
@@ -8304,6 +8240,7 @@ reduces them without incurring seq initialization"
(if (identical? node root)
nil
(set! root node))
+ ;; FIXME: can we figure out something better here?
(if ^boolean (.-val added-leaf?)
(set! count (inc count)))
tcoll))
@@ -8325,6 +8262,7 @@ reduces them without incurring seq initialization"
(if (identical? node root)
nil
(set! root node))
+ ;; FIXME: can we figure out something better here?
(if ^boolean (.-val removed-leaf?)
(set! count (dec count)))
tcoll)))
@@ -9119,7 +9057,7 @@ reduces them without incurring seq initialization"
(if in
(let [in' (next in)]
(if (nil? in')
- (throw (js/Error. (str "No value supplied for key: " (first in))))
+ (throw (js/Error. (str_ "No value supplied for key: " (first in))))
(recur (next in') (assoc! out (first in) (first in')) )))
(persistent! out))))
@@ -9131,29 +9069,20 @@ reduces them without incurring seq initialization"
(.-arr keyvals)
(into-array keyvals))]
(if (odd? (alength arr))
- (throw (js/Error. (str "No value supplied for key: " (last arr))))
+ (throw (js/Error. (str_ "No value supplied for key: " (last arr))))
(.createAsIfByAssoc PersistentArrayMap arr))))
(defn seq-to-map-for-destructuring
"Builds a map from a seq as described in
https://clojure.org/reference/special_forms#keyword-arguments"
[s]
- (if (next s)
- (.createAsIfByAssoc PersistentArrayMap (to-array s))
- (if (seq s) (first s) (.-EMPTY PersistentArrayMap))))
-
-(defn obj-map
- "keyval => key val
- Returns a new object map with supplied mappings."
- [& keyvals]
- (let [ks (array)
- obj (js-obj)]
- (loop [kvs (seq keyvals)]
- (if kvs
- (do (.push ks (first kvs))
- (gobject/set obj (first kvs) (second kvs))
- (recur (nnext kvs)))
- (.fromObject ObjMap ks obj)))))
+ (if ^boolean LITE_MODE
+ (if (next s)
+ (.createAsIfByAssoc ObjMap (to-array s))
+ (if (seq s) (first s) (.-EMPTY ObjMap)))
+ (if (next s)
+ (.createAsIfByAssoc PersistentArrayMap (to-array s))
+ (if (seq s) (first s) (.-EMPTY PersistentArrayMap)))))
(defn sorted-map
"keyval => key val
@@ -9420,7 +9349,10 @@ reduces them without incurring seq initialization"
ICollection
(-conj [coll o]
- (PersistentHashSet. meta (assoc hash-map o nil) nil))
+ (let [m (-assoc hash-map o nil)]
+ (if (identical? m hash-map)
+ coll
+ (PersistentHashSet. meta m nil))))
IEmptyableCollection
(-empty [coll] (-with-meta (.-EMPTY PersistentHashSet) meta))
@@ -9457,7 +9389,10 @@ reduces them without incurring seq initialization"
ISet
(-disjoin [coll v]
- (PersistentHashSet. meta (-dissoc hash-map v) nil))
+ (let [m (-dissoc hash-map v)]
+ (if (identical? m hash-map)
+ coll
+ (PersistentHashSet. meta m nil))))
IFn
(-invoke [coll k]
@@ -9494,7 +9429,7 @@ reduces them without incurring seq initialization"
(dotimes [i len]
(-conj! t (aget items i))
(when-not (= (count t) (inc i))
- (throw (js/Error. (str "Duplicate key: " (aget items i))))))
+ (throw (js/Error. (str_ "Duplicate key: " (aget items i))))))
(-persistent! t))))
(set! (.-createAsIfByAssoc PersistentHashSet)
@@ -9575,7 +9510,10 @@ reduces them without incurring seq initialization"
ICollection
(-conj [coll o]
- (PersistentTreeSet. meta (assoc tree-map o nil) nil))
+ (let [m (-assoc tree-map o nil)]
+ (if (identical? m tree-map)
+ coll
+ (PersistentTreeSet. meta m nil))))
IEmptyableCollection
(-empty [coll] (PersistentTreeSet. meta (-empty tree-map) 0))
@@ -9629,7 +9567,10 @@ reduces them without incurring seq initialization"
ISet
(-disjoin [coll v]
- (PersistentTreeSet. meta (dissoc tree-map v) nil))
+ (let [m (-dissoc tree-map v)]
+ (if (identical? m tree-map)
+ coll
+ (PersistentTreeSet. meta m nil))))
IFn
(-invoke [coll k]
@@ -9743,7 +9684,7 @@ reduces them without incurring seq initialization"
(-name x)
(if (string? x)
x
- (throw (js/Error. (str "Doesn't support name: " x))))))
+ (throw (js/Error. (str_ "Doesn't support name: " x))))))
(defn zipmap
"Returns a map with the keys mapped to the corresponding vals."
@@ -10010,7 +9951,7 @@ reduces them without incurring seq initialization"
IChunkedSeq
(-chunked-first [rng]
- (IntegerRangeChunk. start step (min cnt 32)))
+ (IntegerRangeChunk. start step (unchecked-min cnt 32)))
(-chunked-rest [rng]
(if (<= cnt 32)
()
@@ -10412,7 +10353,7 @@ reduces them without incurring seq initialization"
(cons match-vals
(lazy-seq
(let [post-idx (+ (.-index matches)
- (max 1 (.-length match-str)))]
+ (unchecked-max 1 (.-length match-str)))]
(when (<= post-idx (.-length s))
(re-seq* re (subs s post-idx)))))))))
@@ -10442,13 +10383,13 @@ reduces them without incurring seq initialization"
(-write writer "#")
(do
(-write writer begin)
- (if (zero? (:print-length opts))
+ (if (zero? (pr-opts-len opts))
(when (seq coll)
(-write writer (or (:more-marker opts) "...")))
(do
(when (seq coll)
(print-one (first coll) writer opts))
- (loop [coll (next coll) n (dec (:print-length opts))]
+ (loop [coll (next coll) n (dec (pr-opts-len opts))]
(if (and coll (or (nil? n) (not (zero? n))))
(do
(-write writer sep)
@@ -10460,8 +10401,10 @@ reduces them without incurring seq initialization"
(-write writer end)))))
(defn write-all [writer & ss]
- (doseq [s ss]
- (-write writer s)))
+ (loop [ss (seq ss)]
+ (when-not (nil? ss)
+ (-write writer (first ss))
+ (recur (next ss)))))
(defn string-print [x]
(when (nil? *print-fn*)
@@ -10484,7 +10427,7 @@ reduces them without incurring seq initialization"
(defn ^:private quote-string
[s]
- (str \"
+ (str_ \"
(.replace s (js/RegExp "[\\\\\"\b\f\n\r\t]" "g")
(fn [match] (unchecked-get char-escapes match)))
\"))
@@ -10492,10 +10435,12 @@ reduces them without incurring seq initialization"
(declare print-map)
(defn print-meta? [opts obj]
- (and (boolean (get opts :meta))
+ (and (boolean (pr-opts-meta opts))
(implements? IMeta obj)
(not (nil? (meta obj)))))
+(declare VectorLite)
+
(defn- pr-writer-impl
[obj writer opts]
(cond
@@ -10507,6 +10452,7 @@ reduces them without incurring seq initialization"
(pr-writer (meta obj) writer opts)
(-write writer " "))
(cond
+ ;; FIXME: can we figure out something better here?
;; handle CLJS ctors
^boolean (.-cljs$lang$type obj)
(.cljs$lang$ctorPrWriter obj obj writer opts)
@@ -10516,30 +10462,35 @@ reduces them without incurring seq initialization"
(-pr-writer obj writer opts)
(or (true? obj) (false? obj))
- (-write writer (str obj))
+ (-write writer (str_ obj))
(number? obj)
(-write writer
(cond
- ^boolean (js/isNaN obj) "##NaN"
+ (js/isNaN obj) "##NaN"
(identical? obj js/Number.POSITIVE_INFINITY) "##Inf"
(identical? obj js/Number.NEGATIVE_INFINITY) "##-Inf"
- :else (str obj)))
+ (js/Object.is obj -0.0) "-0.0"
+ :else (str_ obj)))
(object? obj)
(do
(-write writer "#js ")
(print-map
- (map (fn [k]
- (MapEntry. (cond-> k (some? (re-matches #"[A-Za-z_\*\+\?!\-'][\w\*\+\?!\-']*" k)) keyword) (unchecked-get obj k) nil))
- (js-keys obj))
+ (.map
+ (js-keys obj)
+ (fn [k]
+ (MapEntry.
+ (cond-> k (some? (.match k #"^[A-Za-z_\*\+\?!\-'][\w\*\+\?!\-']*$")) keyword)
+ (unchecked-get obj k)
+ nil)))
pr-writer writer opts))
(array? obj)
(pr-sequential-writer writer pr-writer "#js [" " " "]" opts obj)
(string? obj)
- (if (:readably opts)
+ (if (pr-opts-readably opts)
(-write writer (quote-string obj))
(-write writer obj))
@@ -10550,15 +10501,15 @@ reduces them without incurring seq initialization"
name)]
(write-all writer "#object[" name
(if *print-fn-bodies*
- (str " \"" (str obj) "\"")
+ (str_ " \"" (str_ obj) "\"")
"")
"]"))
(instance? js/Date obj)
(let [normalize (fn [n len]
- (loop [ns (str n)]
+ (loop [ns (str_ n)]
(if (< (count ns) len)
- (recur (str "0" ns))
+ (recur (str_ "0" ns))
ns)))]
(write-all writer
"#inst \""
@@ -10586,7 +10537,7 @@ reduces them without incurring seq initialization"
name)]
(if (nil? (. obj -constructor))
(write-all writer "#object[" name "]")
- (write-all writer "#object[" name " " (str obj) "]"))))))))
+ (write-all writer "#object[" name " " (str_ obj) "]"))))))))
(defn- pr-writer
"Prefer this to pr-seq, because it makes the printing function
@@ -10594,14 +10545,16 @@ reduces them without incurring seq initialization"
to a StringBuffer."
[obj writer opts]
(if-let [alt-impl (:alt-impl opts)]
- (alt-impl obj writer (assoc opts :fallback-impl pr-writer-impl))
+ (alt-impl obj writer (-assoc opts :fallback-impl pr-writer-impl))
(pr-writer-impl obj writer opts)))
(defn pr-seq-writer [objs writer opts]
(pr-writer (first objs) writer opts)
- (doseq [obj (next objs)]
- (-write writer " ")
- (pr-writer obj writer opts)))
+ (loop [objs (next objs)]
+ (when-not (nil? objs)
+ (-write writer " ")
+ (pr-writer (first objs) writer opts)
+ (recur (next objs)))))
(defn- pr-sb-with-opts [objs opts]
(let [sb (StringBuffer.)
@@ -10616,7 +10569,7 @@ reduces them without incurring seq initialization"
[objs opts]
(if (empty? objs)
""
- (str (pr-sb-with-opts objs opts))))
+ (str_ (pr-sb-with-opts objs opts))))
(defn prn-str-with-opts
"Same as pr-str-with-opts followed by (newline)"
@@ -10625,7 +10578,7 @@ reduces them without incurring seq initialization"
"\n"
(let [sb (pr-sb-with-opts objs opts)]
(.append sb \newline)
- (str sb))))
+ (str_ sb))))
(defn- pr-with-opts
"Prints a sequence of objects using string-print, observing all
@@ -10638,18 +10591,18 @@ reduces them without incurring seq initialization"
([] (newline nil))
([opts]
(string-print "\n")
- (when (get opts :flush-on-newline)
+ (when (pr-opts-fnl opts)
(flush))))
(defn pr-str
"pr to a string, returning it. Fundamental entrypoint to IPrintWithWriter."
[& objs]
- (pr-str-with-opts objs (pr-opts)))
+ (pr-str-with-opts objs nil))
(defn prn-str
"Same as pr-str followed by (newline)"
[& objs]
- (prn-str-with-opts objs (pr-opts)))
+ (prn-str-with-opts objs nil))
(defn pr
"Prints the object(s) using string-print. Prints the
@@ -10657,38 +10610,42 @@ reduces them without incurring seq initialization"
By default, pr and prn print in a way that objects can be
read by the reader"
[& objs]
- (pr-with-opts objs (pr-opts)))
+ (pr-with-opts objs nil))
(def ^{:doc
"Prints the object(s) using string-print.
print and println produce output for human consumption."}
print
(fn cljs-core-print [& objs]
- (pr-with-opts objs (assoc (pr-opts) :readably false))))
+ (binding [*print-readably* false]
+ (pr-with-opts objs nil))))
(defn print-str
"print to a string, returning it"
[& objs]
- (pr-str-with-opts objs (assoc (pr-opts) :readably false)))
+ (binding [*print-readably* false]
+ (pr-str-with-opts objs nil)))
(defn println
"Same as print followed by (newline)"
[& objs]
- (pr-with-opts objs (assoc (pr-opts) :readably false))
+ (binding [*print-readably* false]
+ (pr-with-opts objs nil))
(when *print-newline*
- (newline (pr-opts))))
+ (newline nil)))
(defn println-str
"println to a string, returning it"
[& objs]
- (prn-str-with-opts objs (assoc (pr-opts) :readably false)))
+ (binding [*print-readably* false]
+ (prn-str-with-opts objs nil)))
(defn prn
"Same as pr followed by (newline)."
[& objs]
- (pr-with-opts objs (pr-opts))
+ (pr-with-opts objs nil)
(when *print-newline*
- (newline (pr-opts))))
+ (newline nil)))
(defn- strip-ns
[named]
@@ -10697,20 +10654,22 @@ reduces them without incurring seq initialization"
(keyword nil (name named))))
(defn- lift-ns
- "Returns [lifted-ns lifted-map] or nil if m can't be lifted."
+ "Returns #js [lifted-ns lifted-map] or nil if m can't be lifted."
[m]
(when *print-namespace-maps*
- (loop [ns nil
- [[k v :as entry] & entries] (seq m)
- lm (empty m)]
- (if entry
- (when (or (keyword? k) (symbol? k))
- (if ns
- (when (= ns (namespace k))
- (recur ns entries (assoc lm (strip-ns k) v)))
- (when-let [new-ns (namespace k)]
- (recur new-ns entries (assoc lm (strip-ns k) v)))))
- [ns lm]))))
+ (let [lm #js []]
+ (loop [ns nil
+ [[k v :as entry] & entries] (seq m)]
+ (if entry
+ (when (or (keyword? k) (symbol? k))
+ (if ns
+ (when (= ns (namespace k))
+ (.push lm (MapEntry. (strip-ns k) v nil))
+ (recur ns entries))
+ (when-let [new-ns (namespace k)]
+ (.push lm (MapEntry. (strip-ns k) v nil))
+ (recur new-ns entries))))
+ #js [ns lm])))))
(defn print-prefix-map [prefix m print-one writer opts]
(pr-sequential-writer
@@ -10719,14 +10678,15 @@ reduces them without incurring seq initialization"
(do (print-one (key e) w opts)
(-write w \space)
(print-one (val e) w opts)))
- (str prefix "{") ", " "}"
+ (str_ prefix "{") ", " "}"
opts (seq m)))
(defn print-map [m print-one writer opts]
- (let [[ns lift-map] (when (map? m)
- (lift-ns m))]
+ (let [ns&lift-map (when (map? m)
+ (lift-ns m))
+ ns (some-> ns&lift-map (aget 0))]
(if ns
- (print-prefix-map (str "#:" ns) lift-map print-one writer opts)
+ (print-prefix-map (str_ "#:" ns) (aget ns&lift-map 1) print-one writer opts)
(print-prefix-map nil m print-one writer opts))))
(extend-protocol IPrintWithWriter
@@ -10787,10 +10747,6 @@ reduces them without incurring seq initialization"
MapEntry
(-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "[" " " "]" opts coll))
- ObjMap
- (-pr-writer [coll writer opts]
- (print-map coll pr-writer writer opts))
-
KeySeq
(-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll))
@@ -10859,43 +10815,43 @@ reduces them without incurring seq initialization"
(-compare [x y]
(if (symbol? y)
(compare-symbols x y)
- (throw (js/Error. (str "Cannot compare " x " to " y)))))
+ (throw (js/Error. (str_ "Cannot compare " x " to " y)))))
Keyword
(-compare [x y]
(if (keyword? y)
(compare-keywords x y)
- (throw (js/Error. (str "Cannot compare " x " to " y)))))
+ (throw (js/Error. (str_ "Cannot compare " x " to " y)))))
Subvec
(-compare [x y]
(if (vector? y)
(compare-indexed x y)
- (throw (js/Error. (str "Cannot compare " x " to " y)))))
+ (throw (js/Error. (str_ "Cannot compare " x " to " y)))))
PersistentVector
(-compare [x y]
(if (vector? y)
(compare-indexed x y)
- (throw (js/Error. (str "Cannot compare " x " to " y)))))
+ (throw (js/Error. (str_ "Cannot compare " x " to " y)))))
MapEntry
(-compare [x y]
(if (vector? y)
(compare-indexed x y)
- (throw (js/Error. (str "Cannot compare " x " to " y)))))
+ (throw (js/Error. (str_ "Cannot compare " x " to " y)))))
BlackNode
(-compare [x y]
(if (vector? y)
(compare-indexed x y)
- (throw (js/Error. (str "Cannot compare " x " to " y)))))
+ (throw (js/Error. (str_ "Cannot compare " x " to " y)))))
RedNode
(-compare [x y]
(if (vector? y)
(compare-indexed x y)
- (throw (js/Error. (str "Cannot compare " x " to " y))))))
+ (throw (js/Error. (str_ "Cannot compare " x " to " y))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Reference Types ;;;;;;;;;;;;;;;;
@@ -10956,7 +10912,7 @@ reduces them without incurring seq initialization"
([prefix-string]
(when (nil? gensym_counter)
(set! gensym_counter (atom 0)))
- (symbol (str prefix-string (swap! gensym_counter inc)))))
+ (symbol (str_ prefix-string (swap! gensym_counter inc)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Delay ;;;;;;;;;;;;;;;;;;;;
@@ -11186,7 +11142,7 @@ reduces them without incurring seq initialization"
(nil? x) nil
(satisfies? IEncodeJS x) (-clj->js x)
(keyword? x) (keyword-fn x)
- (symbol? x) (str x)
+ (symbol? x) (str_ x)
(map? x) (let [m (js-obj)]
(doseq [[k v] x]
(gobject/set m (keyfn k) (thisfn v)))
@@ -11210,7 +11166,7 @@ reduces them without incurring seq initialization"
([x] (js->clj x :keywordize-keys false))
([x & opts]
(let [{:keys [keywordize-keys]} opts
- keyfn (if keywordize-keys keyword str)
+ keyfn (if keywordize-keys keyword str_)
f (fn thisfn [x]
(cond
(satisfies? IEncodeClojure x)
@@ -11314,6 +11270,23 @@ reduces them without incurring seq initialization"
(defn- swap-global-hierarchy! [f & args]
(apply swap! (get-global-hierarchy) f args))
+(defn bases
+ "Returns the immediate prototype of c"
+ [c]
+ (when c
+ (let [s (.getPrototypeOf js/Object c)]
+ (when s
+ (list s)))))
+
+(defn supers
+ "Returns the immediate and indirect prototypes of c, if any"
+ [c]
+ (loop [ret (set (bases c)) cs ret]
+ (if (seq cs)
+ (let [c (first cs) bs (bases c)]
+ (recur (into ret bs) (into (disj cs c) bs)))
+ (not-empty ret))))
+
(defn ^boolean isa?
"Returns true if (= child parent), or child is directly or indirectly derived from
parent, either via a JavaScript type inheritance relationship or a
@@ -11322,17 +11295,17 @@ reduces them without incurring seq initialization"
hierarchy"
([child parent] (isa? @(get-global-hierarchy) child parent))
([h child parent]
- (or (= child parent)
- ;; (and (class? parent) (class? child)
- ;; (. ^Class parent isAssignableFrom child))
- (contains? ((:ancestors h) child) parent)
- ;;(and (class? child) (some #(contains? ((:ancestors h) %) parent) (supers child)))
- (and (vector? parent) (vector? child)
- (== (count parent) (count child))
- (loop [ret true i 0]
- (if (or (not ret) (== i (count parent)))
- ret
- (recur (isa? h (child i) (parent i)) (inc i))))))))
+ (or (= child parent)
+ (and (js-fn? parent) (js-fn? child)
+ (instance? parent child))
+ (contains? ((:ancestors h) child) parent)
+ (and (js-fn? child) (some #(contains? ((:ancestors h) %) parent) (supers child)))
+ (and (vector? parent) (vector? child)
+ (== (count parent) (count child))
+ (loop [ret true i 0]
+ (if (or (not ret) (== i (count parent)))
+ ret
+ (recur (isa? h (child i) (parent i)) (inc i))))))))
(defn parents
"Returns the immediate parents of tag, either via a JavaScript type
@@ -11340,7 +11313,12 @@ reduces them without incurring seq initialization"
must be a hierarchy obtained from make-hierarchy, if not supplied
defaults to the global hierarchy"
([tag] (parents @(get-global-hierarchy) tag))
- ([h tag] (not-empty (get (:parents h) tag))))
+ ([h tag]
+ (not-empty
+ (let [tp (get (:parents h) tag)]
+ (if (js-fn? tag)
+ (into (set (bases tag)) tp)
+ tp)))))
(defn ancestors
"Returns the immediate and indirect parents of tag, either via a JavaScript type
@@ -11348,7 +11326,15 @@ reduces them without incurring seq initialization"
must be a hierarchy obtained from make-hierarchy, if not supplied
defaults to the global hierarchy"
([tag] (ancestors @(get-global-hierarchy) tag))
- ([h tag] (not-empty (get (:ancestors h) tag))))
+ ([h tag]
+ (not-empty
+ (let [ta (get (:ancestors h) tag)]
+ (if (js-fn? tag)
+ (let [superclasses (set (supers tag))]
+ (reduce into superclasses
+ (cons ta
+ (map #(get (:ancestors h) %) superclasses))))
+ ta)))))
(defn descendants
"Returns the immediate and indirect children of tag, through a
@@ -11357,7 +11343,10 @@ reduces them without incurring seq initialization"
hierarchy. Note: does not work on JavaScript type inheritance
relationships."
([tag] (descendants @(get-global-hierarchy) tag))
- ([h tag] (not-empty (get (:descendants h) tag))))
+ ([h tag]
+ (if (js-fn? tag)
+ (throw (js/Error. "Can't get descendants of constructors"))
+ (not-empty (get (:descendants h) tag)))))
(defn derive
"Establishes a parent/child relationship between parent and
@@ -11367,13 +11356,12 @@ reduces them without incurring seq initialization"
supplied defaults to, and modifies, the global hierarchy."
([tag parent]
(assert (namespace parent))
- ;; (assert (or (class? tag) (and (instance? cljs.core.Named tag) (namespace tag))))
+ (assert (or (js-fn? tag) (and (implements? INamed tag) (namespace tag))))
(swap-global-hierarchy! derive tag parent) nil)
([h tag parent]
(assert (not= tag parent))
- ;; (assert (or (class? tag) (instance? clojure.lang.Named tag)))
- ;; (assert (instance? clojure.lang.INamed tag))
- ;; (assert (instance? clojure.lang.INamed parent))
+ (assert (or (js-fn? tag) (implements? INamed tag)))
+ (assert (implements? INamed parent))
(let [tp (:parents h)
td (:descendants h)
ta (:ancestors h)
@@ -11385,9 +11373,9 @@ reduces them without incurring seq initialization"
(or
(when-not (contains? (tp tag) parent)
(when (contains? (ta tag) parent)
- (throw (js/Error. (str tag "already has" parent "as ancestor"))))
+ (throw (js/Error. (str_ tag "already has" parent "as ancestor"))))
(when (contains? (ta parent) tag)
- (throw (js/Error. (str "Cyclic derivation:" parent "has" tag "as ancestor"))))
+ (throw (js/Error. (str_ "Cyclic derivation:" parent "has" tag "as ancestor"))))
{:parents (assoc (:parents h) tag (conj (get tp tag #{}) parent))
:ancestors (tf (:ancestors h) tag td parent ta)
:descendants (tf (:descendants h) parent ta tag td)})
@@ -11450,7 +11438,7 @@ reduces them without incurring seq initialization"
be)]
(when-not (dominates (first be2) k prefer-table @hierarchy)
(throw (js/Error.
- (str "Multiple methods in multimethod '" name
+ (str_ "Multiple methods in multimethod '" name
"' match dispatch value: " dispatch-val " -> " k
" and " (first be2) ", and neither is preferred"))))
be2)
@@ -11481,7 +11469,7 @@ reduces them without incurring seq initialization"
(-dispatch-fn [mf]))
(defn- throw-no-method-error [name dispatch-val]
- (throw (js/Error. (str "No method in multimethod '" name "' for dispatch value: " dispatch-val))))
+ (throw (js/Error. (str_ "No method in multimethod '" name "' for dispatch value: " dispatch-val))))
(deftype MultiFn [name dispatch-fn default-dispatch-val hierarchy
method-table prefer-table method-cache cached-hierarchy]
@@ -11647,7 +11635,7 @@ reduces them without incurring seq initialization"
(-prefer-method [mf dispatch-val-x dispatch-val-y]
(when (prefers* dispatch-val-y dispatch-val-x prefer-table)
- (throw (js/Error. (str "Preference conflict in multimethod '" name "': " dispatch-val-y
+ (throw (js/Error. (str_ "Preference conflict in multimethod '" name "': " dispatch-val-y
" is already preferred to " dispatch-val-x))))
(swap! prefer-table
(fn [old]
@@ -11722,7 +11710,7 @@ reduces them without incurring seq initialization"
IPrintWithWriter
(-pr-writer [_ writer _]
- (-write writer (str "#uuid \"" uuid "\"")))
+ (-write writer (str_ "#uuid \"" uuid "\"")))
IHash
(-hash [this]
@@ -11734,7 +11722,7 @@ reduces them without incurring seq initialization"
(-compare [this other]
(if (instance? UUID other)
(garray/defaultCompare uuid (.-uuid other))
- (throw (js/Error. (str "Cannot compare " this " to " other))))))
+ (throw (js/Error. (str_ "Cannot compare " this " to " other))))))
(defn uuid
"Returns a UUID consistent with the string s."
@@ -11748,14 +11736,14 @@ reduces them without incurring seq initialization"
(letfn [(^string quad-hex []
(let [unpadded-hex ^string (.toString (rand-int 65536) 16)]
(case (count unpadded-hex)
- 1 (str "000" unpadded-hex)
- 2 (str "00" unpadded-hex)
- 3 (str "0" unpadded-hex)
+ 1 (str_ "000" unpadded-hex)
+ 2 (str_ "00" unpadded-hex)
+ 3 (str_ "0" unpadded-hex)
unpadded-hex)))]
(let [ver-tripple-hex ^string (.toString (bit-or 0x4000 (bit-and 0x0fff (rand-int 65536))) 16)
res-tripple-hex ^string (.toString (bit-or 0x8000 (bit-and 0x3fff (rand-int 65536))) 16)]
(uuid
- (str (quad-hex) (quad-hex) "-" (quad-hex) "-"
+ (str_ (quad-hex) (quad-hex) "-" (quad-hex) "-"
ver-tripple-hex "-" res-tripple-hex "-"
(quad-hex) (quad-hex) (quad-hex))))))
@@ -11877,7 +11865,7 @@ reduces them without incurring seq initialization"
(fn [x y]
(cond (pred x y) -1 (pred y x) 1 :else 0)))
-(defn ^boolean special-symbol?
+(defn special-symbol?
"Returns true if x names a special form"
[x]
(contains?
@@ -11929,7 +11917,7 @@ reduces them without incurring seq initialization"
IPrintWithWriter
(-pr-writer [o writer opts]
- (-write writer (str "#" tag " "))
+ (-write writer (str_ "#" tag " "))
(pr-writer form writer opts)))
(defn tagged-literal?
@@ -11982,14 +11970,16 @@ reduces them without incurring seq initialization"
(if (seq ks)
(recur
(next ks)
- (str
+ (str_
(cond-> ret
- (not (identical? ret "")) (str "|"))
+ (not (identical? ret "")) (str_ "|"))
(first ks)))
- (str ret "|\\$"))))))
+ (str_ ret "|\\$"))))))
DEMUNGE_PATTERN)
-(defn- ^string munge-str [name]
+(defn ^string munge-str
+ "Munge string `name` without considering `..` or JavaScript reserved keywords."
+ [name]
(let [sb (StringBuffer.)]
(loop [i 0]
(if (< i (. name -length))
@@ -12001,11 +11991,17 @@ reduces them without incurring seq initialization"
(recur (inc i)))))
(.toString sb)))
-(defn munge [name]
- (let [name' (munge-str (str name))
+(defn munge
+ "Munge symbol or string `name` for safe use in JavaScript.
+
+ - Replaces '..' with '_DOT__DOT_'.
+ - Appends '$' to JavaScript reserved keywords.
+ - Returns a symbol if `name` was a symbol, otherwise a string."
+ [name]
+ (let [name' (munge-str (str_ name))
name' (cond
(identical? name' "..") "_DOT__DOT_"
- (js-reserved? name') (str name' "$")
+ (js-reserved? name') (str_ name' "$")
:else name')]
(if (symbol? name)
(symbol name')
@@ -12020,17 +12016,17 @@ reduces them without incurring seq initialization"
(if-let [match (.exec r munged-name)]
(let [[x] match]
(recur
- (str ret
+ (str_ ret
(.substring munged-name last-match-end
(- (. r -lastIndex) (. x -length)))
(if (identical? x "$") "/" (gobject/get DEMUNGE_MAP x)))
(. r -lastIndex)))
- (str ret
+ (str_ ret
(.substring munged-name last-match-end (.-length munged-name)))))))
(defn demunge [name]
- ((if (symbol? name) symbol str)
- (let [name' (str name)]
+ ((if (symbol? name) symbol str_)
+ (let [name' (str_ name)]
(if (identical? name' "_DOT__DOT_")
".."
(demunge-str name')))))
@@ -12109,14 +12105,14 @@ reduces them without incurring seq initialization"
(deftype Namespace [obj name]
Object
(findInternedVar [this sym]
- (let [k (munge (str sym))]
- (when ^boolean (gobject/containsKey obj k)
- (let [var-sym (symbol (str name) (str sym))
+ (let [k (munge (str_ sym))]
+ (when (gobject/containsKey obj k)
+ (let [var-sym (symbol (str_ name) (str_ sym))
var-meta {:ns this}]
(Var. (ns-lookup obj k) var-sym var-meta)))))
(getName [_] name)
(toString [_]
- (str name))
+ (str_ name))
IEquiv
(-equiv [_ other]
(if (instance? Namespace other)
@@ -12141,10 +12137,10 @@ reduces them without incurring seq initialization"
(defn find-ns-obj
"Bootstrap only."
[ns]
- (let [munged-ns (munge (str ns))
+ (let [munged-ns (munge (str_ ns))
segs (.split munged-ns ".")]
(case *target*
- "nodejs" (if ^boolean js/COMPILED
+ "nodejs" (if ^boolean js/COMPILED
; Under simple optimizations on nodejs, namespaces will be in module
; rather than global scope and must be accessed by a direct call to eval.
; The first segment may refer to an undefined variable, so its evaluation
@@ -12159,7 +12155,7 @@ reduces them without incurring seq initialization"
(next segs))
(find-ns-obj* goog/global segs))
("default" "webworker") (find-ns-obj* goog/global segs)
- (throw (js/Error. (str "find-ns-obj not supported for target " *target*))))))
+ (throw (js/Error. (str_ "find-ns-obj not supported for target " *target*))))))
(defn ns-interns*
"Returns a map of the intern mappings for the namespace.
@@ -12171,7 +12167,7 @@ reduces them without incurring seq initialization"
(let [var-sym (symbol (demunge k))]
(assoc ret
var-sym (Var. #(gobject/get ns-obj k)
- (symbol (str sym) (str var-sym)) {:ns ns}))))]
+ (symbol (str_ sym) (str_ var-sym)) {:ns ns}))))]
(reduce step {} (js-keys ns-obj)))))
(defn create-ns
@@ -12202,9 +12198,9 @@ reduces them without incurring seq initialization"
[ns]
(when (nil? NS_CACHE)
(set! NS_CACHE (atom {})))
- (let [ns-str (str ns)
- ns (if (not ^boolean (gstring/contains ns-str "$macros"))
- (symbol (str ns-str "$macros"))
+ (let [ns-str (str_ ns)
+ ns (if (not (gstring/contains ns-str "$macros"))
+ (symbol (str_ ns-str "$macros"))
ns)
the-ns (get @NS_CACHE ns)]
(if-not (nil? the-ns)
@@ -12227,15 +12223,10 @@ reduces them without incurring seq initialization"
[x]
(instance? goog.Uri x))
-(defn ^boolean NaN?
- "Returns true if num is NaN, else false"
- [val]
- (js/isNaN val))
-
(defn ^:private parsing-err
"Construct message for parsing for non-string parsing error"
[val]
- (str "Expected string, got: " (if (nil? val) "nil" (goog/typeOf val))))
+ (str_ "Expected string, got: " (if (nil? val) "nil" (goog/typeOf val))))
(defn ^number parse-long
"Parse string of decimal digits with optional leading -/+ and return an
@@ -12256,6 +12247,7 @@ reduces them without incurring seq initialization"
[s]
(if (string? s)
(cond
+ ;; FIXME: another cases worth thinking about
^boolean (re-matches #"[\x00-\x20]*[+-]?NaN[\x00-\x20]*" s) ##NaN
^boolean (re-matches
#"[\x00-\x20]*[+-]?(Infinity|((\d+\.?\d*|\.\d+)([eE][+-]?\d+)?)[dDfF]?)[\x00-\x20]*"
@@ -12331,3 +12323,826 @@ reduces them without incurring seq initialization"
(identical? "window" *global*) (set! goog/global js/window)
(identical? "self" *global*) (set! goog/global js/self)
(identical? "global" *global*) (set! goog/global js/global)))
+
+;; -----------------------------------------------------------------------------
+;; Original 2011 Copy-on-Write Types
+
+;;; VectorLite
+
+(deftype VectorLiteIterator [arr ^:mutable i]
+ Object
+ (hasNext [_]
+ (< i (alength arr)))
+ (next [_]
+ (let [x (aget arr i)]
+ (set! i (inc i))
+ x)))
+
+(deftype VectorLite [meta array ^:mutable __hash]
+ Object
+ (toString [coll]
+ (pr-str* coll))
+ (equiv [coll other]
+ (-equiv coll other))
+ (indexOf [coll x start]
+ (let [start (if (nil? start) 0 start)
+ len (-count coll)]
+ (if (>= start len)
+ -1
+ (loop [idx (cond
+ (pos? start) start
+ (neg? start) (unchecked-max 0 (+ start len))
+ :else start)]
+ (if (< idx len)
+ (if (= (-nth coll idx) x)
+ idx
+ (recur (inc idx)))
+ -1)))))
+ (lastIndexOf [coll x start]
+ (let [start (if (nil? start) (alength array) start)
+ len (-count coll)]
+ (if (zero? len)
+ -1
+ (loop [idx (cond
+ (pos? start) (unchecked-min (dec len) start)
+ (neg? start) (+ len start)
+ :else start)]
+ (if (>= idx 0)
+ (if (= (-nth coll idx) x)
+ idx
+ (recur (dec idx)))
+ -1)))))
+
+ IWithMeta
+ (-with-meta [coll new-meta]
+ (if (identical? new-meta meta)
+ coll
+ (VectorLite. new-meta array __hash)))
+
+ ICloneable
+ (-clone [coll] (VectorLite. meta array __hash))
+
+ IMeta
+ (-meta [coll] meta)
+
+ IStack
+ (-peek [coll]
+ (let [count (alength array)]
+ (when (> count 0)
+ (aget array (dec count)))))
+ (-pop [coll]
+ (if (> (alength array) 0)
+ (let [new-array (aclone array)]
+ (. new-array (pop))
+ (VectorLite. meta new-array nil))
+ (throw (js/Error. "Can't pop empty vector"))))
+
+ ICollection
+ (-conj [coll o]
+ (let [new-array (aclone array)]
+ (.push new-array o)
+ (VectorLite. meta new-array nil)))
+
+ IEmptyableCollection
+ (-empty [coll] (with-meta (. VectorLite -EMPTY) meta))
+
+ ISequential
+ IEquiv
+ (-equiv [coll other] (equiv-sequential coll other))
+
+ IHash
+ (-hash [coll] (hash-ordered-coll coll))
+
+ ISeqable
+ (-seq [coll]
+ (when (> (alength array) 0)
+ (prim-seq array)))
+
+ ICounted
+ (-count [coll] (alength array))
+
+ IIndexed
+ (-nth [coll n]
+ (if (and (<= 0 n) (< n (alength array)))
+ (aget array (int n))
+ (throw (js/Error. (str "No item " n " in vector of length " (alength array))))))
+ (-nth [coll n not-found]
+ (if (and (<= 0 n) (< n (alength array)))
+ (aget array (int n))
+ not-found))
+
+ ILookup
+ (-lookup [coll k]
+ (when (number? k)
+ (-nth coll k nil)))
+ (-lookup [coll k not-found]
+ (if (number? k)
+ (-nth coll k not-found)
+ not-found))
+
+ IAssociative
+ (-assoc [coll k v]
+ (if (number? k)
+ (let [new-array (aclone array)]
+ (aset new-array k v)
+ (VectorLite. meta new-array nil))
+ (throw (js/Error. "Vector's key for assoc must be a number."))))
+ (-contains-key? [coll k]
+ (if (integer? k)
+ (and (<= 0 k) (< k (alength array)))
+ false))
+
+ IVector
+ (-assoc-n [coll n val] (-assoc coll n val))
+
+ IReversible
+ (-rseq [coll]
+ (let [cnt (alength array)]
+ (when (pos? cnt)
+ (RSeq. coll (dec cnt) nil))))
+
+ IReduce
+ (-reduce [v f]
+ (array-reduce array f))
+ (-reduce [v f start]
+ (array-reduce array f start))
+
+ IKVReduce
+ (-kv-reduce [v f init]
+ (let [len (alength array)]
+ (loop [i 0 init init]
+ (if (< i len)
+ (let [init (f init i (aget array i))]
+ (if (reduced? init)
+ @init
+ (recur (inc i) init)))
+ init))))
+
+ IDrop
+ (-drop [v n]
+ (let [cnt (alength array)]
+ (if (< n cnt)
+ (prim-seq array n)
+ nil)))
+
+ IComparable
+ (-compare [x y]
+ (if (vector? y)
+ (compare-indexed x y)
+ (throw (js/Error. "Cannot compare with Vector"))))
+
+ IFn
+ (-invoke [coll k]
+ (if (number? k)
+ (-nth coll k)
+ (throw (js/Error. "Key must be integer"))))
+
+ IEditableCollection
+ (-as-transient [coll]
+ coll)
+
+ ITransientCollection
+ (-conj! [coll val]
+ (-conj coll val))
+ (-persistent! [coll]
+ coll)
+
+ ITransientAssociative
+ (-assoc! [tcoll key val]
+ (-assoc-n! tcoll key val))
+
+ ITransientVector
+ (-assoc-n! [tcoll key val]
+ (if (number? key)
+ (-assoc-n tcoll key val)
+ (throw (js/Error. "Vector's key for assoc! must be a number."))))
+
+ (-pop! [tcoll]
+ (-pop tcoll))
+
+ IIterable
+ (-iterator [coll]
+ (VectorLiteIterator. array 0))
+
+ IPrintWithWriter
+ (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "[" " " "]" opts coll)))
+
+(es6-iterable VectorLite)
+
+(set! (. VectorLite -EMPTY) (VectorLite. nil (array) nil))
+
+(set! (. VectorLite -fromArray) (fn [xs] (VectorLite. nil xs nil)))
+
+(defn vector-lite
+ ":lite-mode version of vector, not intended to be used directly."
+ [& args]
+ (if (and (instance? IndexedSeq args) (zero? (.-i args)))
+ (.fromArray VectorLite (aclone (.-arr args)))
+ (VectorLite. nil (into-array args) nil)))
+
+(defn vec-lite
+ ":lite-mode version of vec, not intended to be used directly."
+ [coll]
+ (cond
+ (map-entry? coll)
+ [(key coll) (val coll)]
+
+ (vector? coll)
+ (with-meta coll nil)
+
+ (array? coll)
+ (.fromArray VectorLite coll)
+
+ :else
+ (into [] coll)))
+
+; The keys field is an array of all keys of this map, in no particular
+; order. Any string, keyword, or symbol key is used as a property name
+; to store the value in strobj. If a key is assoc'ed when that same
+; key already exists in strobj, the old value is overwritten. If a
+; non-string key is assoc'ed, return a HashMap object instead.
+
+(defn- obj-map-compare-keys [a b]
+ (let [a (hash a)
+ b (hash b)]
+ (cond
+ (< a b) -1
+ (> a b) 1
+ :else 0)))
+
+(defn- obj-clone [obj ks]
+ (let [new-obj (js-obj)
+ l (alength ks)]
+ (loop [i 0]
+ (when (< i l)
+ (let [k (aget ks i)]
+ (gobject/set new-obj k (gobject/get obj k))
+ (recur (inc i)))))
+ new-obj))
+
+(declare hash-map-lite HashMapLite)
+
+(defn- keyword->obj-map-key
+ [k]
+ (str "\uFDD0" "'" (. k -fqn)))
+
+(defn- obj-map-key->keyword
+ [k]
+ (if (.startsWith k "\uFDD0")
+ (keyword (.substring k 2 (. k -length)))
+ k))
+
+(defn- scan-array [incr k array]
+ (let [len (alength array)]
+ (loop [i 0]
+ (when (< i len)
+ (if (identical? k (aget array i))
+ i
+ (recur (+ i incr)))))))
+
+(deftype ObjMapIterator [strkeys strobj ^:mutable i]
+ Object
+ (hasNext [_]
+ (< i (alength strkeys)))
+ (next [_]
+ (let [k (aget strkeys i)]
+ (set! i (inc i))
+ (MapEntry. (obj-map-key->keyword k) (unchecked-get strobj k) nil))))
+
+(deftype ObjMap [meta strkeys strobj ^:mutable __hash]
+ Object
+ (toString [coll]
+ (pr-str* coll))
+ (keys [coll]
+ (es6-iterator
+ (prim-seq
+ (.map (.sort strkeys obj-map-compare-keys)
+ obj-map-key->keyword))))
+ (entries [coll]
+ (es6-entries-iterator (-seq coll)))
+ (values [coll]
+ (es6-iterator
+ (prim-seq
+ (.map (.sort strkeys obj-map-compare-keys)
+ #(unchecked-get strobj %)))))
+ (has [coll k]
+ (contains? coll k))
+ (get [coll k not-found]
+ (-lookup coll k not-found))
+ (forEach [coll f]
+ (.forEach (.sort strkeys obj-map-compare-keys)
+ #(f (unchecked-get strobj %) (obj-map-key->keyword %))))
+
+ IWithMeta
+ (-with-meta [coll meta] (ObjMap. meta strkeys strobj __hash))
+
+ IMeta
+ (-meta [coll] meta)
+
+ ICloneable
+ (-clone [coll] (ObjMap. meta strkeys strobj __hash))
+
+ ICollection
+ (-conj [coll entry]
+ (if (vector? entry)
+ (-assoc coll (-nth entry 0) (-nth entry 1))
+ (reduce -conj coll entry)))
+
+ IEmptyableCollection
+ (-empty [coll] (-with-meta (. ObjMap -EMPTY) meta))
+
+ IEquiv
+ (-equiv [coll other] (equiv-map coll other))
+
+ IHash
+ (-hash [coll] (caching-hash coll hash-unordered-coll __hash))
+
+ ISeqable
+ (-seq [coll]
+ (when (pos? (alength strkeys))
+ (prim-seq
+ (.map (.sort strkeys obj-map-compare-keys)
+ #(MapEntry. (obj-map-key->keyword %) (unchecked-get strobj %) nil)))))
+
+ ICounted
+ (-count [coll] (alength strkeys))
+
+ ILookup
+ (-lookup [coll k] (-lookup coll k nil))
+ (-lookup [coll k not-found]
+ (let [k (if-not (keyword? k) k (keyword->obj-map-key k))]
+ (if (and (string? k)
+ (not (nil? (scan-array 1 k strkeys))))
+ (unchecked-get strobj k)
+ not-found)))
+
+ IAssociative
+ (-assoc [coll k v]
+ (let [k (if-not (keyword? k) k (keyword->obj-map-key k))]
+ (if (string? k)
+ (if-not (nil? (scan-array 1 k strkeys))
+ (if (identical? v (gobject/get strobj k))
+ coll
+ ; overwrite
+ (let [new-strobj (obj-clone strobj strkeys)]
+ (gobject/set new-strobj k v)
+ (ObjMap. meta strkeys new-strobj nil)))
+ ; append
+ (let [new-strobj (obj-clone strobj strkeys)
+ new-keys (aclone strkeys)]
+ (gobject/set new-strobj k v)
+ (.push new-keys k)
+ (ObjMap. meta new-keys new-strobj nil)))
+ ; non-string key. game over.
+ (-with-meta
+ (-kv-reduce coll
+ (fn [ret k v]
+ (-assoc ret k v))
+ (hash-map-lite k v))
+ meta))))
+ (-contains-key? [coll k]
+ (let [k (if-not (keyword? k) k (keyword->obj-map-key k))]
+ (if (and (string? k)
+ (not (nil? (scan-array 1 k strkeys))))
+ true
+ false)))
+
+ IFind
+ (-find [coll k]
+ (let [k' (if-not (keyword? k) k (keyword->obj-map-key k))]
+ (when (and (string? k')
+ (not (nil? (scan-array 1 k' strkeys))))
+ (MapEntry. k (unchecked-get strobj k') nil))))
+
+ IKVReduce
+ (-kv-reduce [coll f init]
+ (let [len (alength strkeys)]
+ (loop [keys (.sort strkeys obj-map-compare-keys)
+ init init]
+ (if (seq keys)
+ (let [k (first keys)
+ init (f init (obj-map-key->keyword k) (unchecked-get strobj k))]
+ (if (reduced? init)
+ @init
+ (recur (rest keys) init)))
+ init))))
+
+ IIterable
+ (-iterator [coll]
+ (ObjMapIterator. strkeys strobj 0))
+
+ IReduce
+ (-reduce [coll f]
+ (iter-reduce coll f))
+ (-reduce [coll f start]
+ (iter-reduce coll f start))
+
+ IMap
+ (-dissoc [coll k]
+ (let [k (if-not (keyword? k) k (keyword->obj-map-key k))]
+ (if (and (string? k)
+ (not (nil? (scan-array 1 k strkeys))))
+ (let [new-keys (aclone strkeys)
+ new-strobj (obj-clone strobj strkeys)]
+ (.splice new-keys (scan-array 1 k new-keys) 1)
+ (js-delete new-strobj k)
+ (ObjMap. meta new-keys new-strobj nil))
+ coll))) ; key not found, return coll unchanged
+
+ IFn
+ (-invoke [coll k]
+ (-lookup coll k))
+ (-invoke [coll k not-found]
+ (-lookup coll k not-found))
+
+ IEditableCollection
+ (-as-transient [coll]
+ coll)
+
+ ITransientCollection
+ (-conj! [coll val]
+ (-conj coll val))
+ (-persistent! [coll]
+ coll)
+
+ ITransientAssociative
+ (-assoc! [coll key val]
+ (-assoc coll key val))
+
+ ITransientMap
+ (-dissoc! [coll key]
+ (-dissoc coll key))
+
+ IPrintWithWriter
+ (-pr-writer [coll writer opts]
+ (print-map coll pr-writer writer opts)))
+
+(es6-iterable ObjMap)
+
+(set! (. ObjMap -EMPTY) (ObjMap. nil (array) (js-obj) empty-unordered-hash))
+
+(set! (. ObjMap -fromObject) (fn [ks obj] (ObjMap. nil ks obj nil)))
+
+(defn obj-map
+ ":lite-mode simple key hash-map, not intended to be used directly."
+ [& keyvals]
+ (let [ks (array)
+ obj (js-obj)]
+ (loop [kvs (seq keyvals)]
+ (if kvs
+ (let [k (-> kvs first keyword->obj-map-key)]
+ (.push ks k)
+ (gobject/set obj k (second kvs))
+ (recur (nnext kvs)))
+ (.fromObject ObjMap ks obj)))))
+
+(set! (. ObjMap -createAsIfByAssoc)
+ (fn [init]
+ ;; check trailing element
+ (let [len (alength init)
+ has-trailing? (== 1 (bit-and len 1))
+ init (if has-trailing?
+ (pam-grow-seed-array init
+ (into {} (aget init (dec len))))
+ init)
+ len (alength init)]
+ (loop [i 0 ret {}]
+ (if (< i len)
+ (recur (+ i 2) (assoc ret (aget init i) (aget init (inc i))))
+ ret)))))
+
+(defn- scan-array-equiv [incr k array]
+ (let [len (alength array)]
+ (loop [i 0]
+ (when (< i len)
+ (if (= k (aget array i))
+ i
+ (recur (+ i incr)))))))
+
+; The keys field is an array of all keys of this map, in no particular
+; order. Each key is hashed and the result used as a property name of
+; hashobj. Each values in hashobj is actually a bucket in order to handle hash
+; collisions. A bucket is an array of alternating keys (not their hashes) and
+; vals.
+(deftype HashMapLite [meta count hashobj ^:mutable __hash]
+ Object
+ (toString [coll]
+ (pr-str* coll))
+ (keys [coll]
+ (let [arr (. (-seq coll) -arr)]
+ (es6-iterator (prim-seq (.map arr -key (-seq coll))))))
+ (entries [coll]
+ (es6-entries-iterator (-seq coll)))
+ (values [coll]
+ (let [arr (. (-seq coll) -arr)]
+ (es6-iterator (prim-seq (.map arr -val (-seq coll))))))
+ (has [coll k]
+ (contains? coll k))
+ (get [coll k not-found]
+ (-lookup coll k not-found))
+ (forEach [coll f]
+ (let [xs (-seq coll)]
+ (when-not (nil? xs)
+ (.forEach (.-arr xs)
+ #(f (-val %) (-key %))))))
+
+ IWithMeta
+ (-with-meta [coll meta] (HashMapLite. meta count hashobj __hash))
+
+ IMeta
+ (-meta [coll] meta)
+
+ ICloneable
+ (-clone [coll] (HashMapLite. meta count hashobj __hash))
+
+ ICollection
+ (-conj [coll entry]
+ (if (vector? entry)
+ (-assoc coll (-nth entry 0) (-nth entry 1))
+ (reduce -conj coll entry)))
+
+ IEmptyableCollection
+ (-empty [coll] (with-meta (. HashMapLite -EMPTY) meta))
+
+ IEquiv
+ (-equiv [coll other] (equiv-map coll other))
+
+ IHash
+ (-hash [coll] (caching-hash coll hash-unordered-coll __hash))
+
+ ISeqable
+ (-seq [coll]
+ (when (pos? count)
+ (let [hashes (.sort (js-keys hashobj))
+ cnt (alength hashes)
+ arr (array)]
+ (loop [i 0]
+ (if (< i cnt)
+ (let [bckt (unchecked-get hashobj (aget hashes i))
+ len (alength bckt)]
+ (loop [j 0]
+ (when (< j len)
+ (do
+ (.push arr (MapEntry. (aget bckt j) (aget bckt (inc j)) nil))
+ (recur (+ j 2)))))
+ (recur (inc i)))
+ (prim-seq arr))))))
+
+ ICounted
+ (-count [coll] count)
+
+ ILookup
+ (-lookup [coll k] (-lookup coll k nil))
+ (-lookup [coll k not-found]
+ (let [bucket (unchecked-get hashobj (hash k))
+ i (when bucket (scan-array-equiv 2 k bucket))]
+ (if (some? i)
+ (aget bucket (inc i))
+ not-found)))
+
+ IAssociative
+ (-assoc [coll k v]
+ (let [h (hash k)
+ bucket (unchecked-get hashobj h)]
+ (if (some? bucket)
+ (let [new-bucket (aclone bucket)
+ new-hashobj (gobject/clone hashobj)
+ i (scan-array-equiv 2 k new-bucket)]
+ (aset new-hashobj h new-bucket)
+ (if (some? i)
+ (if (identical? v (aget new-bucket (inc i)))
+ coll
+ (do
+ ; found key, replace
+ (aset new-bucket (inc i) v)
+ (HashMapLite. meta count new-hashobj nil)))
+ (do
+ ; did not find key, append
+ (.push new-bucket k v)
+ (HashMapLite. meta (inc count) new-hashobj nil))))
+ (let [new-hashobj (gobject/clone hashobj)]
+ ; did not find bucket
+ (unchecked-set new-hashobj h (array k v))
+ (HashMapLite. meta (inc count) new-hashobj nil)))))
+ (-contains-key? [coll k]
+ (let [bucket (unchecked-get hashobj (hash k))
+ i (when bucket (scan-array-equiv 2 k bucket))]
+ (if (some? i)
+ true
+ false)))
+
+ IMap
+ (-dissoc [coll k]
+ (let [h (hash k)
+ bucket (unchecked-get hashobj h)
+ i (when bucket (scan-array-equiv 2 k bucket))]
+ (if (some? i)
+ (let [new-hashobj (gobject/clone hashobj)]
+ (if (> 3 (alength bucket))
+ (js-delete new-hashobj h)
+ (let [new-bucket (aclone bucket)]
+ (.splice new-bucket i 2)
+ (unchecked-set new-hashobj h new-bucket)))
+ (HashMapLite. meta (dec count) new-hashobj nil))
+ ; key not found, return coll unchanged
+ coll)))
+
+ IFn
+ (-invoke [coll k]
+ (-lookup coll k))
+ (-invoke [coll k not-found]
+ (-lookup coll k not-found))
+
+ IEditableCollection
+ (-as-transient [coll]
+ coll)
+
+ ITransientCollection
+ (-conj! [coll val]
+ (-conj coll val))
+ (-persistent! [coll]
+ coll)
+
+ ITransientAssociative
+ (-assoc! [coll key val]
+ (-assoc coll key val))
+
+ ITransientMap
+ (-dissoc! [coll key]
+ (-dissoc coll key))
+
+ IIterable
+ (-iterator [coll]
+ (let [xs (-seq coll)]
+ (if (some? xs)
+ (-iterator xs)
+ (nil-iter))))
+
+ IKVReduce
+ (-kv-reduce [coll f init]
+ (let [hashes (.sort (js-keys hashobj))
+ ilen (alength hashes)]
+ (loop [i 0 init init]
+ (if (< i ilen)
+ (let [bckt (unchecked-get hashobj (aget hashes i))
+ jlen (alength bckt)
+ init (loop [j 0 init init]
+ (if (< j jlen)
+ (let [init (f init (aget bckt j) (aget bckt (inc j)))]
+ (if (reduced? init)
+ init
+ (recur (+ j 2) init)))
+ init))]
+ (if (reduced? init)
+ @init
+ (recur (inc i) init)))
+ init))))
+
+ IPrintWithWriter
+ (-pr-writer [coll writer opts]
+ (print-map coll pr-writer writer opts)))
+
+(es6-iterable HashMapLite)
+
+(set! (. HashMapLite -EMPTY) (HashMapLite. nil 0 (js-obj) empty-unordered-hash))
+
+(set! (. HashMapLite -fromArrays) (fn [ks vs]
+ (let [len (.-length ks)]
+ (loop [i 0, out (. HashMapLite -EMPTY)]
+ (if (< i len)
+ (recur (inc i) (assoc out (aget ks i) (aget vs i)))
+ out)))))
+
+(defn hash-map-lite
+ ":lite-mode version of hash-map, not intended to be used directly."
+ [& keyvals]
+ (loop [in (seq keyvals), out (. HashMapLite -EMPTY)]
+ (if in
+ (recur (nnext in) (-assoc out (first in) (second in)))
+ out)))
+
+(deftype SetLite [meta hash-map ^:mutable __hash]
+ Object
+ (toString [coll]
+ (pr-str* coll))
+ (keys [coll]
+ (es6-iterator (-seq coll)))
+ (entries [coll]
+ (es6-set-entries-iterator (-seq coll)))
+ (values [coll]
+ (es6-iterator (-seq coll)))
+ (has [coll k]
+ (contains? coll k))
+ (forEach [coll f]
+ (let [xs (-seq hash-map)]
+ (when (some? xs)
+ (.forEach (.-arr xs)
+ #(f (-val %) (-key %))))))
+
+ IWithMeta
+ (-with-meta [coll new-meta]
+ (if (identical? new-meta meta)
+ coll
+ (SetLite. new-meta hash-map __hash)))
+
+ IMeta
+ (-meta [coll] meta)
+
+ ICloneable
+ (-clone [coll] (SetLite. meta hash-map __hash))
+
+ ICollection
+ (-conj [coll o]
+ (let [new-hash-map (assoc hash-map o o)]
+ (if (identical? new-hash-map hash-map)
+ coll
+ (SetLite. meta new-hash-map nil))))
+
+ IEmptyableCollection
+ (-empty [coll] (with-meta (. SetLite -EMPTY) meta))
+
+ IEquiv
+ (-equiv [coll other]
+ (and
+ (set? other)
+ (= (-count coll) (count other))
+ (every? #(contains? coll %)
+ other)))
+
+ IHash
+ (-hash [coll] (caching-hash coll hash-unordered-coll __hash))
+
+ ISeqable
+ (-seq [coll]
+ (let [xs (-seq hash-map)]
+ (when (some? xs)
+ (prim-seq (.map (.-arr xs) (fn [kv] (-key kv)))))))
+
+ ICounted
+ (-count [coll]
+ (let [xs (-seq coll)]
+ (if (some? xs)
+ (-count xs)
+ 0)))
+
+ ILookup
+ (-lookup [coll v]
+ (-lookup coll v nil))
+ (-lookup [coll v not-found]
+ (if (-contains-key? hash-map v)
+ (-lookup hash-map v)
+ not-found))
+
+ ISet
+ (-disjoin [coll v]
+ (let [new-hash-map (-dissoc hash-map v)]
+ (if (identical? new-hash-map hash-map)
+ coll
+ (SetLite. meta new-hash-map nil))))
+
+ IEditableCollection
+ (-as-transient [coll]
+ coll)
+
+ ITransientCollection
+ (-conj! [coll val]
+ (-conj coll val))
+ (-persistent! [coll]
+ coll)
+
+ ITransientSet
+ (-disjoin! [coll key]
+ (-disjoin coll key))
+
+ IFn
+ (-invoke [coll k]
+ (-lookup coll k))
+ (-invoke [coll k not-found]
+ (-lookup coll k not-found))
+
+ IIterable
+ (-iterator [coll]
+ (let [xs (-seq coll)]
+ (if (some? xs)
+ (-iterator xs)
+ (nil-iter))))
+
+ IPrintWithWriter
+ (-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "#{" " " "}" opts coll)))
+
+(es6-iterable SetLite)
+
+(set! (. SetLite -EMPTY) (SetLite. nil (. HashMapLite -EMPTY) empty-unordered-hash))
+
+(defn set-lite
+ ":lite-mode version of set, not intended ot be used directly."
+ [coll]
+ (if (set? coll)
+ (-with-meta coll nil)
+ (let [in (seq coll)]
+ (if (nil? in)
+ #{}
+ (loop [in in out (. SetLite -EMPTY)]
+ (if-not (nil? in)
+ (recur (next in) (-conj out (first in)))
+ out))))))
diff --git a/src/main/cljs/cljs/js.cljs b/src/main/cljs/cljs/js.cljs
index 076350951a..bb8e7fbc42 100644
--- a/src/main/cljs/cljs/js.cljs
+++ b/src/main/cljs/cljs/js.cljs
@@ -651,14 +651,14 @@
(.append sb "null;")))
(defn- global-exports-side-effects
- [bound-vars sb deps ns-name emit-nil-result?]
+ [bound-vars sb deps ns-name opts]
(let [{:keys [js-dependency-index]} @(:*compiler* bound-vars)]
(doseq [dep deps]
(let [{:keys [global-exports]} (get js-dependency-index (name dep))]
(.append sb
(with-out-str
- (comp/emit-global-export ns-name global-exports dep)))))
- (when (and (seq deps) emit-nil-result?)
+ (comp/emit-global-export ns-name global-exports dep opts)))))
+ (when (and (seq deps) (:def-emits-var opts))
(.append sb "null;"))))
(defn- trampoline-safe
@@ -835,8 +835,7 @@
(node-side-effects bound-vars sb node-deps ns-name (:def-emits-var opts)))
(global-exports-side-effects bound-vars sb
(filter ana/dep-has-global-exports? (:deps ast))
- ns-name
- (:def-emits-var opts))
+ ns-name opts)
(cb (try
{:ns ns-name :value ((:*eval-fn* bound-vars) {:source (.toString sb)})}
(catch :default cause
@@ -1102,8 +1101,7 @@
(node-side-effects bound-vars sb node-deps ns-name (:def-emits-var opts)))
(global-exports-side-effects bound-vars sb
(filter ana/dep-has-global-exports? (:deps ast))
- ns-name
- (:def-emits-var opts))
+ ns-name opts)
(trampoline compile-loop ns'))))))
(do
(env/with-compiler-env (assoc @(:*compiler* bound-vars) :options opts)
diff --git a/src/main/cljs/cljs/proxy.cljs b/src/main/cljs/cljs/proxy.cljs
new file mode 100644
index 0000000000..23a85a9f77
--- /dev/null
+++ b/src/main/cljs/cljs/proxy.cljs
@@ -0,0 +1,184 @@
+; 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 cljs.proxy
+ (:refer-global :only [isNaN Proxy Symbol])
+ (:require [cljs.proxy.impl :refer [SimpleCache MapIterator]]))
+
+(defn- write-through [f]
+ (let [cache (SimpleCache. #js {} 0)]
+ (fn [x]
+ (let [v (.get cache x)]
+ (if (some? v)
+ v
+ (.set cache x (f x)))))))
+
+(def ^{:private true}
+ desc
+ #js {:configurable true
+ :enumerable true})
+
+(defn builder
+ "EXPERIMENTAL: Returns a JavaScript Proxy ctor fn with the provided
+ key-fn. Invoking the returned fn on ClojureScript maps and vectors
+ will returned proxied values that can be used transparently as
+ JavaScript objects and arrays:
+
+ (def proxy (builder))
+
+ (def proxied-map (proxy {:foo 1 :bar 2}))
+ (goog.object/get proxied-map \"foo\") ;; => 1
+
+ (def proxied-vec (proxy [1 2 3 4]))
+ (aget proxied-vec 1) ;; => 2
+
+ Access patterns from JavaScript on these proxied values will lazily,
+ recursively return further proxied values:
+
+ (def nested-proxies (proxy [{:foo 1 :bar 2}]))
+ (-> nested-proxies (aget 0) (goog.object/get \"foo\")) ;; => 1
+
+ Note key-fn is only used for proxied ClojureScript maps. This
+ function should map strings to the appropriate key
+ representation. If unspecified, key-fn defaults to keyword. All maps
+ proxied from the same ctor fn will share the same key-fn cache.
+
+ A cache-fn may be suppled to override the default cache. This fn
+ should take key-fn and return a memoized version."
+ ([]
+ (builder keyword))
+ ([key-fn]
+ (builder keyword write-through))
+ ([key-fn cache-fn]
+ (js* "var __ctor")
+ (let [cache-key-fn (cache-fn key-fn)
+ vec-handler #js {:get (fn [^cljs.core/IIndexed target prop receiver]
+ (cond
+ (identical? "length" prop)
+ (-count ^cljs.core/ICounted target)
+
+ (identical? (. Symbol -iterator) prop)
+ (fn []
+ (MapIterator.
+ ((.bind (unchecked-get target prop) target)) js/__ctor))
+
+ :else
+ (let [n (js* "+~{}" prop)]
+ (when (and (number? n)
+ (not (isNaN n)))
+ (js/__ctor (-nth target n nil))))))
+
+ :has (fn [^cljs.core/IAssociative target prop]
+ (cond
+ (identical? prop "length") true
+
+ (identical? (. Symbol -iterator) prop) true
+
+ :else
+ (let [n (js* "+~{}" prop)]
+ (and (number? n)
+ (not (isNaN n))
+ (<= 0 n)
+ (< n (-count ^cljs.core/ICounted target))))))
+
+ :getPrototypeOf
+ (fn [target] nil)
+
+ :ownKeys
+ (fn [target] #js ["length"])
+
+ :getOwnPropertyDescriptor
+ (fn [target prop] desc)}
+ map-handler #js {:get (fn [^cljs.core/ILookup target prop receiver]
+ (js/__ctor (-lookup target (cache-key-fn prop))))
+
+ :has (fn [^cljs.core/IAssociative target prop]
+ (-contains-key? target (cache-key-fn prop)))
+
+ :getPrototypeOf
+ (fn [target] nil)
+
+ :ownKeys
+ (fn [target]
+ (when (nil? (.-cljs$cachedOwnKeys target))
+ (set! (. target -cljs$cachedOwnKeys)
+ (into-array (map -name (keys target)))))
+ (.-cljs$cachedOwnKeys target))
+
+ :getOwnPropertyDescriptor
+ (fn [target prop] desc)}
+ __ctor (fn [target]
+ (cond
+ (implements? IMap target) (Proxy. target map-handler)
+ (implements? IVector target) (Proxy. target vec-handler)
+ :else target))]
+ __ctor)))
+
+(def ^{:doc "Default proxy for maps and vectors."}
+ proxy (builder))
+
+(comment
+
+ (def c (SimpleCache. #js {} 0))
+ (.set c "foo" :foo)
+ (.get c "foo")
+ (.-cnt c)
+ (.clear c)
+ (.get c "foo")
+
+ (def kw (write-through keyword))
+ (kw "foo")
+
+ (time
+ (dotimes [i 1e6]
+ (kw "foo")))
+
+ (time
+ (dotimes [i 1e6]
+ (keyword "foo")))
+
+ (def proxy (builder))
+
+ (def raw-map {:foo 1 :bar 2})
+ (def proxied-map (proxy {:foo 1 :bar 2}))
+
+ (require '[goog.object :as gobj])
+ (gobj/get proxied-map "foo")
+ (gobj/get proxied-map "bar")
+ (gobj/getKeys proxied-map)
+ (.keys js/Object proxied-map)
+
+ (time
+ (dotimes [i 1e7]
+ (unchecked-get proxied-map "foo")))
+
+ (def k :foo)
+ (time
+ (dotimes [i 1e7]
+ (get raw-map k)))
+
+ (def proxied-vec (proxy [1 2 3 4]))
+ (alength proxied-vec)
+ (time
+ (dotimes [i 1e6]
+ (alength proxied-vec)))
+
+ (nth [1 2 3 4] 1)
+
+ (aget proxied-vec 1)
+
+ (time
+ (dotimes [i 1e7]
+ (aget proxied-vec 1)))
+
+ (def proxied-deep (proxy [{:foo "Hello"}]))
+ (-> proxied-deep (aget 0) (unchecked-get "foo"))
+
+ (aget ((cljs.proxy/builder) [{}]) 0)
+
+)
diff --git a/src/main/cljs/cljs/proxy/impl.cljs b/src/main/cljs/cljs/proxy/impl.cljs
new file mode 100644
index 0000000000..c476529314
--- /dev/null
+++ b/src/main/cljs/cljs/proxy/impl.cljs
@@ -0,0 +1,32 @@
+; 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 cljs.proxy.impl)
+
+(deftype SimpleCache [^:mutable obj ^:mutable cnt]
+ Object
+ (set [this k v]
+ (when (== cnt 1024)
+ (.clear this))
+ (unchecked-set obj k v)
+ (set! cnt (inc cnt))
+ v)
+ (get [this k]
+ (unchecked-get obj k))
+ (clear [this]
+ (set! obj #js {})
+ (set! cnt 0)))
+
+(deftype MapIterator [^:mutable iter f]
+ Object
+ (next [_]
+ (let [x (.next iter)]
+ (if-not ^boolean (. x -done)
+ #js {:value (f (. x -value))
+ :done false}
+ x))))
diff --git a/src/main/cljs/cljs/spec/alpha.cljs b/src/main/cljs/cljs/spec/alpha.cljs
index c88079f5f8..740262aad7 100644
--- a/src/main/cljs/cljs/spec/alpha.cljs
+++ b/src/main/cljs/cljs/spec/alpha.cljs
@@ -148,6 +148,10 @@
(specize* ([s] (spec-impl s s nil nil))
([s form] (spec-impl form s nil nil)))
+ SetLite
+ (specize* ([s] (spec-impl s s nil nil))
+ ([s form] (spec-impl form s nil nil)))
+
default
(specize*
([o]
diff --git a/src/main/cljs/cljs/test.cljc b/src/main/cljs/cljs/test.cljc
index 5afb03ca20..27776ad4b9 100644
--- a/src/main/cljs/cljs/test.cljc
+++ b/src/main/cljs/cljs/test.cljc
@@ -262,7 +262,7 @@
cljs.test/IAsyncTest
cljs.core/IFn
(~'-invoke [_# ~done]
- ~@body)))
+ ((^:async fn [] ~@body)))))
;; =============================================================================
;; Running Tests
diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc
index 8c61c45867..c63b857f2d 100644
--- a/src/main/clojure/cljs/analyzer.cljc
+++ b/src/main/clojure/cljs/analyzer.cljc
@@ -16,6 +16,7 @@
#?(:clj (:require [cljs.analyzer.impl :as impl]
[cljs.analyzer.impl.namespaces :as nses]
[cljs.analyzer.passes.and-or :as and-or]
+ [cljs.analyzer.passes.lite :as lite]
[cljs.env :as env :refer [ensure]]
[cljs.externs :as externs]
[cljs.js-deps :as deps]
@@ -30,6 +31,7 @@
:cljs (:require [cljs.analyzer.impl :as impl]
[cljs.analyzer.impl.namespaces :as nses]
[cljs.analyzer.passes.and-or :as and-or]
+ [cljs.analyzer.passes.lite :as lite]
[cljs.env :as env]
[cljs.reader :as edn]
[cljs.tagged-literals :as tags]
@@ -492,6 +494,12 @@
(def ^:dynamic *cljs-warning-handlers*
[default-warning-handler])
+(defn lite-mode? []
+ (get-in @env/*compiler* [:options :lite-mode]))
+
+(defn elide-to-string? []
+ (get-in @env/*compiler* [:options :elide-to-string]))
+
#?(:clj
(defmacro with-warning-handlers [handlers & body]
`(binding [*cljs-warning-handlers* ~handlers]
@@ -977,13 +985,16 @@
(or (= 'js x)
(= "js" (namespace x)))))
+(defn ->pre [x]
+ (->> (string/split (name x) #"\.") (map symbol)))
+
(defn normalize-js-tag [x]
;; if not 'js, assume constructor
(if-not (= 'js x)
- (with-meta 'js
- {:prefix (conj (->> (string/split (name x) #"\.")
- (map symbol) vec)
- 'prototype)})
+ (let [props (->pre x)
+ [xs y] ((juxt butlast last) props)]
+ (with-meta 'js
+ {:prefix (vec (concat xs [(with-meta y {:ctor true})]))}))
x))
(defn ->type-set
@@ -1030,46 +1041,89 @@
boolean Boolean
symbol Symbol})
-(defn has-extern?*
+(defn resolve-extern
+ "Given a foreign js property list, return a resolved js property list and the
+ extern var info"
+ ([pre]
+ (resolve-extern pre (get-externs)))
([pre externs]
- (let [pre (if-some [me (find
- (get-in externs '[Window prototype])
- (first pre))]
- (if-some [tag (-> me first meta :tag)]
- (into [tag 'prototype] (next pre))
- pre)
- pre)]
- (has-extern?* pre externs externs)))
- ([pre externs top]
+ (resolve-extern pre externs externs {:resolved []}))
+ ([pre externs top ret]
(cond
- (empty? pre) true
+ (empty? pre) ret
:else
(let [x (first pre)
me (find externs x)]
(cond
- (not me) false
+ (not me) nil
:else
(let [[x' externs'] me
- xmeta (meta x')]
- (if (and (= 'Function (:tag xmeta)) (:ctor xmeta))
- (or (has-extern?* (into '[prototype] (next pre)) externs' top)
- (has-extern?* (next pre) externs' top)
- ;; check base type if it exists
- (when-let [super (:super xmeta)]
- (has-extern?* (into [super] (next pre)) externs top)))
- (recur (next pre) externs' top))))))))
+ info' (meta x')
+ ret (cond-> ret
+ ;; we only care about var info for the last property
+ ;; also if we already added it, don't override it
+ ;; because we're now resolving type information
+ ;; not instance information anymore
+ ;; i.e. [console] -> [Console] but :tag is Console _not_ Function vs.
+ ;; [console log] -> [Console prototype log] where :tag is Function
+ (and (empty? (next pre))
+x (not (contains? ret :info)))
+ (assoc :info info'))]
+ ;; handle actual occurrences of types, i.e. `Console`
+ (if (and (or (:ctor info') (:iface info')) (= 'Function (:tag info')))
+ (or
+ ;; then check for "static" property
+ (resolve-extern (next pre) externs' top
+ (update ret :resolved conj x))
+
+ ;; first look for a property on the prototype
+ (resolve-extern (into '[prototype] (next pre)) externs' top
+ (update ret :resolved conj x))
+
+ ;; finally check the super class if there is one
+ (when-let [super (:super info')]
+ (resolve-extern (into [super] (next pre)) externs top
+ (assoc ret :resolved []))))
+
+ (or
+ ;; If the tag of the property isn't Function or undefined,
+ ;; try to resolve it similar to the super case above,
+ ;; this handles singleton cases like `console`
+ (let [tag (:tag info')]
+ (when (and tag (not (contains? '#{Function undefined} tag)))
+ ;; check prefix first, during cljs.externs parsing we always generate prefixes
+ ;; for tags because of types like webCrypto.Crypto
+ (resolve-extern (into (or (-> tag meta :prefix) [tag]) (next pre)) externs top
+ (assoc ret :resolved []))))
+
+ ;; assume static property
+ (recur (next pre) externs' top
+ (update ret :resolved conj x))))))))))
+
+(defn normalize-unresolved-prefix
+ [pre]
+ (cond-> pre
+ (< 1 (count pre))
+ (cond->
+ (-> pre pop peek meta :ctor)
+ (-> pop
+ (conj 'prototype)
+ (conj (peek pre))))))
+
+(defn has-extern?*
+ [pre externs]
+ (boolean (resolve-extern pre externs)))
(defn has-extern?
([pre]
(has-extern? pre (get-externs)))
([pre externs]
(or (has-extern?* pre externs)
- (when (= 1 (count pre))
- (let [x (first pre)]
- (or (get-in externs (conj '[Window prototype] x))
- (get-in externs (conj '[Number] x)))))
(-> (last pre) str (string/starts-with? "cljs$")))))
+(defn lift-tag-to-js [tag]
+ (symbol "js" (str (alias->type tag tag))))
+
(defn js-tag
([pre]
(js-tag pre :tag))
@@ -1078,12 +1132,13 @@
([pre tag-type externs]
(js-tag pre tag-type externs externs))
([pre tag-type externs top]
- (when-let [[p externs' :as me] (find externs (first pre))]
- (let [tag (-> p meta tag-type)]
- (if (= (count pre) 1)
- (when tag (symbol "js" (str (alias->type tag tag))))
- (or (js-tag (next pre) tag-type externs' top)
- (js-tag (into '[prototype] (next pre)) tag-type (get top tag) top)))))))
+ (when-let [tag (get-in (resolve-extern pre externs) [:info tag-type])]
+ (case tag
+ ;; don't lift these, analyze-dot will raise them for analysis
+ ;; representing these types as js/Foo is a hassle as it widens the
+ ;; return types unnecessarily i.e. #{boolean js/Boolean}
+ (boolean number string) tag
+ (lift-tag-to-js tag)))))
(defn dotted-symbol? [sym]
(let [s (str sym)]
@@ -1167,9 +1222,12 @@
(defmethod resolve* :goog-module
[env sym full-ns current-ns]
- {:name (symbol (str current-ns) (str (munge-goog-module-lib full-ns) "." (name sym)))
- :ns current-ns
- :op :var})
+ (let [sym-ast (gets @env/*compiler* ::namespaces full-ns :defs (symbol (name sym)))]
+ (merge sym-ast
+ {:name (symbol (str current-ns) (str (munge-goog-module-lib full-ns) "." (name sym)))
+ :ns current-ns
+ :op :var
+ :unaliased-name (symbol (str full-ns) (name sym))})))
(defmethod resolve* :global
[env sym full-ns current-ns]
@@ -1255,6 +1313,10 @@
{:name sym}
lb))
+(defn qualified->dotted
+ [sym]
+ (symbol (str (namespace sym) "." (name sym))))
+
(defn resolve-var
"Resolve a var. Accepts a side-effecting confirm fn for producing
warnings about unresolved vars."
@@ -1274,8 +1336,9 @@
(assoc shadowed-by-local :op :local))
:else
- (let [pre (->> (string/split (name sym) #"\.") (map symbol) vec)]
- (when (and (not (has-extern? pre))
+ (let [pre (->> (string/split (name sym) #"\.") (map symbol) vec)
+ res (resolve-extern (->> (string/split (name sym) #"\.") (map symbol) vec))]
+ (when (and (not res)
;; ignore exists? usage
(not (-> sym meta ::no-resolve)))
(swap! env/*compiler* update-in
@@ -1284,10 +1347,12 @@
{:name sym
:op :js-var
:ns 'js
- :tag (with-meta (or (js-tag pre) (:tag (meta sym)) 'js) {:prefix pre})}
+ :tag (with-meta (or (js-tag pre) (:tag (meta sym)) 'js)
+ {:prefix pre
+ :ctor (-> res :info :ctor)})}
(when-let [ret-tag (js-tag pre :ret-tag)]
{:js-fn-var true
- :ret-tag ret-tag})))))
+ :ret-tag ret-tag})))))
(let [s (str sym)
lb (handle-symbol-local sym (get locals sym))
current-ns (-> env :ns :name)]
@@ -1295,20 +1360,25 @@
(some? lb) (assoc lb :op :local)
(some? (namespace sym))
- (let [ns (namespace sym)
- ns (if #?(:clj (= "clojure.core" ns)
- :cljs (identical? "clojure.core" ns))
- "cljs.core"
- ns)
- full-ns (resolve-ns-alias env ns
- (or (and (js-module-exists? ns)
- (gets @env/*compiler* :js-module-index ns :name))
- (symbol ns)))]
- (when (some? confirm)
- (when (not= current-ns full-ns)
- (confirm-ns env full-ns))
- (confirm env full-ns (symbol (name sym))))
- (resolve* env sym full-ns current-ns))
+ (let [ns (namespace sym)]
+ (if-let [resolved (and (nil? (resolve-ns-alias env ns nil))
+ (not (dotted-symbol? ns))
+ (resolve-var env (symbol ns) nil false)
+ (resolve-var env (qualified->dotted sym) nil false))]
+ resolved
+ (let [ns (if #?(:clj (= "clojure.core" ns)
+ :cljs (identical? "clojure.core" ns))
+ "cljs.core"
+ ns)
+ full-ns (resolve-ns-alias env ns
+ (or (and (js-module-exists? ns)
+ (gets @env/*compiler* :js-module-index ns :name))
+ (symbol ns)))]
+ (when (some? confirm)
+ (when (not= current-ns full-ns)
+ (confirm-ns env full-ns))
+ (confirm env full-ns (symbol (name sym))))
+ (resolve* env sym full-ns current-ns))))
(dotted-symbol? sym)
(let [idx (.indexOf s ".")
@@ -1568,6 +1638,7 @@
:throw impl/IGNORE_SYM
:let (infer-tag env (:body ast))
:loop (infer-tag env (:body ast))
+ :try (infer-tag env (:body ast))
:do (infer-tag env (:ret ast))
:fn-method (infer-tag env (:body ast))
:def (infer-tag env (:init ast))
@@ -2243,6 +2314,12 @@
meths)
locals (:locals env)
name-var (fn-name-var env locals name)
+ async (or
+ ;; NOTE: adding async on fn form turns it into a MetaFn which isn't great for interop, let's discourage it - Michiel Borkent
+ #_(:async (meta form))
+ (:async (meta name))
+ (:async (meta (first form))))
+ env (assoc env :async async)
env (if (some? name)
(update-in env [:fn-scope] conj name-var)
env)
@@ -2584,12 +2661,12 @@
:children [:expr]}))
(def js-prim-ctor->tag
- '{js/Object object
- js/String string
- js/Array array
- js/Number number
+ '{js/Object object
+ js/String string
+ js/Array array
+ js/Number number
js/Function function
- js/Boolean boolean})
+ js/Boolean boolean})
(defn prim-ctor?
"Test whether a tag is a constructor for a JS primitive"
@@ -2719,6 +2796,17 @@
(if (and (.exists cljcf) (.isFile cljcf))
cljcf))))))
+(defn external-dep?
+ "Returns true if the library is an :external? foreign dep. This means no source is provided
+ for the library, i.e. it will be provided by some script tag on the page, or loaded by some
+ other means into the JS execution environment."
+ #?(:cljs {:tag boolean})
+ [dep]
+ (let [js-index (:js-dependency-index @env/*compiler*)]
+ (if-some [[_ {:keys [foreign external?]}] (find js-index (name (-> dep lib&sublib first)))]
+ (and foreign external?)
+ false)))
+
(defn foreign-dep?
#?(:cljs {:tag boolean})
[dep]
@@ -2766,13 +2854,20 @@
(error env
(error-message :undeclared-ns {:ns-sym dep :js-provide (name dep)}))))))))))))
+(defn global-ns? [x]
+ (and (symbol? x)
+ (or (= 'js x)
+ (= "js" (namespace x)))))
+
(defn missing-use? [lib sym cenv]
- (let [js-lib (get-in cenv [:js-dependency-index (name lib)])]
- (and (= (get-in cenv [::namespaces lib :defs sym] ::not-found) ::not-found)
- (not (= (get js-lib :group) :goog))
- (not (get js-lib :closure-lib))
- (not (node-module-dep? lib))
- (not (dep-has-global-exports? lib)))))
+ ;; ignore globals referred via :refer-global
+ (when-not (global-ns? lib)
+ (let [js-lib (get-in cenv [:js-dependency-index (name lib)])]
+ (and (= (get-in cenv [::namespaces lib :defs sym] ::not-found) ::not-found)
+ (not (= (get js-lib :group) :goog))
+ (not (get js-lib :closure-lib))
+ (not (node-module-dep? lib))
+ (not (dep-has-global-exports? lib))))))
(defn missing-rename? [sym cenv]
(let [lib (symbol (namespace sym))
@@ -2985,6 +3080,91 @@
ret
(recur fs ret true)))))
+(defn parse-global-refer-spec
+ [env args]
+ (let [xs (filter #(-> % first (= :refer-global)) args)
+ cnt (count xs)]
+ (cond
+ (> cnt 1)
+ (throw (error env "Only one :refer-global form is allowed per namespace definition"))
+
+ (== cnt 1)
+ (let [[_ & {:keys [only rename] :as parsed-spec}] (first xs)
+ only-set (set only)
+ err-str "Only (:refer-global :only [names]) and optionally `:rename {from to}` specs supported.
+ :rename symbols must be present in :only"]
+ (when-not (or (empty? only)
+ (and (vector? only)
+ (every? symbol only)))
+ (throw (error env err-str)))
+ (when-not (or (empty? rename)
+ (and (map? rename)
+ (every? symbol (mapcat identity rename))
+ (every? only-set (keys rename))))
+ (throw (error env (str err-str (pr-str parsed-spec)))))
+ (when-not (every? #{:only :rename} (keys parsed-spec))
+ (throw (error env (str err-str (pr-str parsed-spec)))))
+ {:use (zipmap (if rename (remove rename only)
+ only) (repeat 'js))
+ :rename (into {}
+ (map (fn [[orig new-name]]
+ [new-name (symbol "js" (str orig))]))
+ rename)}))))
+
+(defn parse-global-require-spec
+ [env cenv deps aliases spec]
+ (if (or (symbol? spec) (string? spec))
+ (recur env cenv deps aliases [spec])
+ (do
+ (basic-validate-ns-spec env false spec)
+ (let [[lib & opts] spec
+ {alias :as referred :refer renamed :rename
+ :or {alias (if (string? lib)
+ (symbol (munge lib))
+ lib)}}
+ (apply hash-map opts)
+ referred-without-renamed (seq (remove (set (keys renamed)) referred))
+ [rk uk renk] [:require :use :rename]]
+ (when-not (or (symbol? alias) (nil? alias))
+ (throw
+ (error env
+ (parse-ns-error-msg spec
+ ":as must be followed by a symbol in :require / :require-macros"))))
+ (when (some? alias)
+ (let [lib' ((:fns @aliases) alias)]
+ (when (and (some? lib') (not= lib lib'))
+ (throw (error env (parse-ns-error-msg spec ":as alias must be unique"))))
+ (when (= alias 'js)
+ (when-not (= lib (get-in @aliases [:fns 'js])) ; warn only once
+ (warning :js-used-as-alias env {:spec spec})))
+ (swap! aliases update-in [:fns] conj [alias lib])))
+ (when-not (or (and (sequential? referred)
+ (every? symbol? referred))
+ (nil? referred))
+ (throw
+ (error env
+ (parse-ns-error-msg spec
+ ":refer must be followed by a sequence of symbols in :require / :require-macros"))))
+ (swap! deps conj lib)
+ (let [ret (merge
+ (when (some? alias)
+ {rk (merge {alias lib} {lib lib})})
+ (when (some? referred-without-renamed)
+ {uk (apply hash-map (interleave referred-without-renamed (repeat lib)))})
+ (when (some? renamed)
+ {renk (reduce (fn [m [original renamed]]
+ (when-not (some #{original} referred)
+ (throw (error env
+ (str "Renamed symbol " original " not referred"))))
+ (assoc m renamed (symbol (str lib) (str original))))
+ {} renamed)}))]
+ (swap! cenv assoc-in [:js-dependency-index (str lib)]
+ {:external? true
+ :foreign true
+ :provides [(str lib)]
+ :global-exports {lib lib}})
+ ret)))))
+
(defn parse-require-spec [env macros? deps aliases spec]
(if (or (symbol? spec) (string? spec))
(recur env macros? deps aliases [spec])
@@ -3238,6 +3418,10 @@
(select-keys new deep-merge-keys))))
new))
+(def ns-spec-cases
+ #{:use :use-macros :require :require-macros
+ :import :refer-global :require-global})
+
(defmethod parse 'ns
[_ env [_ name & args :as form] _ opts]
(when-not *allow-ns*
@@ -3272,6 +3456,7 @@
core-renames (reduce (fn [m [original renamed]]
(assoc m renamed (symbol "cljs.core" (str original))))
{} core-renames)
+ {global-uses :use global-renames :rename} (parse-global-refer-spec env args)
deps (atom [])
;; as-aliases can only be used *once* because they are about the reader
aliases (atom {:fns as-aliases :macros as-aliases})
@@ -3281,8 +3466,9 @@
(partial use->require env))
:use-macros (comp (partial parse-require-spec env true deps aliases)
(partial use->require env))
- :import (partial parse-import-spec env deps)}
- valid-forms (atom #{:use :use-macros :require :require-macros :import})
+ :import (partial parse-import-spec env deps)
+ :require-global #(parse-global-require-spec env env/*compiler* deps aliases %)}
+ valid-forms (atom #{:use :use-macros :require :require-macros :require-global :import})
reload (atom {:use nil :require nil :use-macros nil :require-macros nil})
reloads (atom {})
{uses :use requires :require renames :rename
@@ -3290,8 +3476,8 @@
rename-macros :rename-macros imports :import :as params}
(reduce
(fn [m [k & libs :as libspec]]
- (when-not (#{:use :use-macros :require :require-macros :import} k)
- (throw (error env (str "Only :refer-clojure, :require, :require-macros, :use, :use-macros, and :import libspecs supported. Got " libspec " instead."))))
+ (when-not (#{:use :use-macros :require :require-macros :require-global :import} k)
+ (throw (error env (str "Only :refer-clojure, :require, :require-macros, :use, :use-macros, :require-global and :import libspecs supported. Got " libspec " instead."))))
(when-not (@valid-forms k)
(throw (error env (str "Only one " k " form is allowed per namespace definition"))))
(swap! valid-forms disj k)
@@ -3308,7 +3494,7 @@
(apply merge-with merge m
(map (spec-parsers k)
(remove #{:reload :reload-all} libs))))
- {} (remove (fn [[r]] (= r :refer-clojure)) args))
+ {} (remove (fn [[r]] (#{:refer-clojure :refer-global} r)) args))
;; patch `require-macros` and `use-macros` in Bootstrap for namespaces
;; that require their own macros
#?@(:cljs [[require-macros use-macros]
@@ -3330,9 +3516,9 @@
:use-macros use-macros
:require-macros require-macros
:rename-macros rename-macros
- :uses uses
+ :uses (merge uses global-uses)
:requires requires
- :renames (merge renames core-renames)
+ :renames (merge renames core-renames global-renames)
:imports imports}]
(swap! env/*compiler* update-in [::namespaces name] merge ns-info)
(merge {:op :ns
@@ -3372,6 +3558,7 @@
core-renames (reduce (fn [m [original renamed]]
(assoc m renamed (symbol "cljs.core" (str original))))
{} core-renames)
+ {global-uses :use global-renames :rename} (parse-global-refer-spec env args)
deps (atom [])
;; as-aliases can only be used *once* because they are about the reader
aliases (atom {:fns as-aliases :macros as-aliases})
@@ -3381,7 +3568,8 @@
(partial use->require env))
:use-macros (comp (partial parse-require-spec env true deps aliases)
(partial use->require env))
- :import (partial parse-import-spec env deps)}
+ :import (partial parse-import-spec env deps)
+ :require-global #(parse-global-require-spec env env/*compiler* deps aliases %)}
reload (atom {:use nil :require nil :use-macros nil :require-macros nil})
reloads (atom {})
{uses :use requires :require renames :rename
@@ -3402,7 +3590,7 @@
(apply merge-with merge m
(map (spec-parsers k)
(remove #{:reload :reload-all} libs))))
- {} (remove (fn [[r]] (= r :refer-clojure)) args))]
+ {} (remove (fn [[r]] (#{:refer-clojure :refer-global} r)) args))]
(set! *cljs-ns* name)
(let [require-info
{:as-aliases as-aliases
@@ -3411,9 +3599,9 @@
:use-macros use-macros
:require-macros require-macros
:rename-macros rename-macros
- :uses uses
+ :uses (merge uses global-uses)
:requires requires
- :renames (merge renames core-renames)
+ :renames (merge renames core-renames global-renames)
:imports imports}]
(swap! env/*compiler* update-in [::namespaces name] merge-ns-info require-info env)
(merge {:op :ns*
@@ -3542,13 +3730,25 @@
(list* '. dot-form) " with classification "
(classify-dot-form dot-form))))))
+;; this only for a smaller set of types that we want to infer
+;; we don't generally want to consider function for example, these
+;; specific cases are ones we either try to optimize or validate
+(def ^{:private true}
+ tag->js-prim-ctor
+ '{string js/String
+ array js/Array
+ number js/Number
+ boolean js/Boolean})
+
(defn analyze-dot [env target field member+ form]
(let [v [target field member+]
{:keys [dot-action target method field args]} (build-dot-form v)
enve (assoc env :context :expr)
targetexpr (analyze enve target)
form-meta (meta form)
- target-tag (:tag targetexpr)
+ target-tag (as-> (:tag targetexpr) $
+ (or (some-> $ meta :ctor lift-tag-to-js)
+ (tag->js-prim-ctor $ $)))
prop (or field method)
tag (or (:tag form-meta)
(and (js-tag? target-tag)
@@ -3580,7 +3780,8 @@
(let [pre (-> tag meta :prefix)]
(when-not (has-extern? pre)
(swap! env/*compiler* update-in
- (into [::namespaces (-> env :ns :name) :externs] pre) merge {}))))
+ (into [::namespaces (-> env :ns :name) :externs]
+ (normalize-unresolved-prefix pre)) merge {}))))
(case dot-action
::access (let [children [:target]]
{:op :host-field
@@ -3823,15 +4024,15 @@
bind-args? (and HO-invoke?
(not (all-values? args)))]
(when ^boolean fn-var?
- (let [{^boolean variadic :variadic? :keys [max-fixed-arity method-params name ns macro]} (:info fexpr)]
- ;; don't warn about invalid arity when when compiling a macros namespace
+ (let [{^boolean variadic :variadic? :keys [max-fixed-arity method-params name unaliased-name ns macro]} (:info fexpr)]
+ ;; don't warn about invalid arity when compiling a macros namespace
;; that requires itself, as that code is not meant to be executed in the
;; `$macros` ns - António Monteiro
(when (and #?(:cljs (not (and (gstring/endsWith (str cur-ns) "$macros")
(symbol-identical? cur-ns ns)
(true? macro))))
(invalid-arity? argc method-params variadic max-fixed-arity))
- (warning :fn-arity env {:name name :argc argc}))))
+ (warning :fn-arity env {:name (or unaliased-name name) :argc argc}))))
(when (and kw? (not (or (== 1 argc) (== 2 argc))))
(warning :fn-arity env {:name (first form) :argc argc}))
(let [deprecated? (-> fexpr :info :deprecated)
@@ -3882,7 +4083,10 @@
{:op :host-field
:env (:env expr)
:form (list '. prefix field)
- :target (desugar-dotted-expr (-> expr
+ ;; goog.module vars get converted to the form of
+ ;; current.ns/goog$module.theDef, we need to dissoc
+ ;; actual extern var info so we get something well-formed
+ :target (desugar-dotted-expr (-> (dissoc expr :info)
(assoc :name prefix
:form prefix)
(dissoc :tag)
@@ -3890,6 +4094,9 @@
(assoc-in [:env :context] :expr)))
:field field
:tag (:tag expr)
+ ;; in the case of goog.module var if there is :info,
+ ;; we need to adopt it now as this is where :ret-tag info lives
+ :info (:info expr)
:children [:target]})
expr)
;:var
@@ -3920,6 +4127,7 @@
(select-keys lb [:name :local :arg-id :variadic? :init])))
(let [sym-meta (meta sym)
sym-ns (namespace sym)
+ sym-name (name sym)
cur-ns (str (-> env :ns :name))
;; when compiling a macros namespace that requires itself, we need
;; to resolve calls to `my-ns.core/foo` to `my-ns.core$macros/foo`
@@ -3930,21 +4138,36 @@
(not (gstring/endsWith sym-ns "$macros"))
(= sym-ns (subs cur-ns 0 (- (count cur-ns) 7))))
(symbol (str sym-ns "$macros") (name sym))
- sym)])
- info (if-not (contains? sym-meta ::analyzed)
+ sym)])]
+ (if (and sym-ns
+ (nil? (resolve-ns-alias env sym-ns nil))
+ (not= ".." sym-name) ;; special case `..` macro in self-hosted
+ (or (= "new" sym-name)
+ (string/starts-with? sym-name ".")))
+ (merge
+ {:op :qualified-method
+ :env env
+ :form sym
+ :class (analyze-symbol (assoc env :context :expr) (symbol sym-ns))}
+ (if (= "new" sym-name)
+ {:kind :new
+ :name (symbol sym-name)}
+ {:kind :method
+ :name (symbol (subs sym-name 1))}))
+ (let [info (if-not (contains? sym-meta ::analyzed)
(resolve-existing-var env sym)
(resolve-var env sym))]
- (assert (:op info) (:op info))
- (desugar-dotted-expr
- (if-not (true? (:def-var env))
- (merge
- (assoc ret :info info)
- (select-keys info [:op :name :ns :tag])
- (when-let [const-expr (:const-expr info)]
- {:const-expr const-expr}))
- (let [info (resolve-var env sym)]
- (merge (assoc ret :op :var :info info)
- (select-keys info [:op :name :ns :tag]))))))))))
+ (assert (:op info) (:op info))
+ (desugar-dotted-expr
+ (if-not (true? (:def-var env))
+ (merge
+ (assoc ret :info info)
+ (select-keys info [:op :name :ns :tag])
+ (when-let [const-expr (:const-expr info)]
+ {:const-expr const-expr}))
+ (let [info (resolve-var env sym)]
+ (merge (assoc ret :op :var :info info)
+ (select-keys info [:op :name :ns :tag]))))))))))))
(defn excluded?
#?(:cljs {:tag boolean})
@@ -3999,8 +4222,10 @@
(if (and (some? nsym) (symbol? nsym))
(.findInternedVar ^clojure.lang.Namespace
#?(:clj (find-ns nsym) :cljs (find-macros-ns nsym)) sym)
- (.findInternedVar ^clojure.lang.Namespace
- #?(:clj (find-ns 'cljs.core) :cljs (find-macros-ns impl/CLJS_CORE_MACROS_SYM)) sym)))))))
+ ;; can't be done as compiler pass because macros get to run first
+ (when-not (and (lite-mode?) (= 'vector sym))
+ (.findInternedVar ^clojure.lang.Namespace
+ #?(:clj (find-ns 'cljs.core) :cljs (find-macros-ns impl/CLJS_CORE_MACROS_SYM)) sym))))))))
(defn get-expander
"Given a sym, a symbol identifying a macro, and env, an analysis environment
@@ -4379,10 +4604,8 @@
:cljs [infer-type and-or/optimize check-invoke-arg-types]))
(defn analyze* [env form name opts]
- (let [passes *passes*
- passes (if (nil? passes)
- default-passes
- passes)
+ (let [passes (cond-> (or *passes* default-passes)
+ (lite-mode?) (conj lite/use-lite-types))
form (if (instance? LazySeq form)
(if (seq form) form ())
form)
@@ -4641,7 +4864,7 @@
(defn build-affecting-options [opts]
(select-keys opts
[:static-fns :fn-invoke-direct :optimize-constants :elide-asserts :target :nodejs-rt
- :cache-key :checked-arrays :language-out :optimizations])))
+ :cache-key :checked-arrays :language-out :optimizations :lite-mode :elide-to-string])))
#?(:clj
(defn build-affecting-options-sha [path opts]
diff --git a/src/main/clojure/cljs/analyzer/api.cljc b/src/main/clojure/cljs/analyzer/api.cljc
index 2d143a42b6..2fa4f2a134 100644
--- a/src/main/clojure/cljs/analyzer/api.cljc
+++ b/src/main/clojure/cljs/analyzer/api.cljc
@@ -218,6 +218,15 @@
([state]
(keys (get @state ::ana/namespaces))))
+(defn resolve-extern
+ "Given a symbol attempt to look it up in the provided externs"
+ ([sym]
+ (resolve-extern env/*compiler* sym))
+ ([state sym]
+ (let [pre (ana/->pre sym)]
+ (env/with-compiler-env state
+ (:info (ana/resolve-extern pre))))))
+
(defn find-ns
"Given a namespace return the corresponding namespace analysis map. Analagous
to clojure.core/find-ns."
diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index da4f2761f4..9087151c95 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -321,7 +321,8 @@ present"
reopts (merge repl-env-options (select-keys opts [:main :output-dir]))
_ (when (or ana/*verbose* (:verbose opts))
(util/debug-prn "REPL env options:" (pr-str reopts)))
- renv (apply (target->repl-env (:target options) repl-env) (mapcat identity reopts))]
+ renv (-> (apply (target->repl-env (:target options) repl-env) (mapcat identity reopts))
+ (assoc :compiler-opts opts))]
(repl/repl* renv
(assoc opts
::repl/fast-initial-prompt?
diff --git a/src/main/clojure/cljs/closure.clj b/src/main/clojure/cljs/closure.clj
index b215573f6d..55e7181f0b 100644
--- a/src/main/clojure/cljs/closure.clj
+++ b/src/main/clojure/cljs/closure.clj
@@ -185,7 +185,6 @@
:report-unknown-types DiagnosticGroups/REPORT_UNKNOWN_TYPES
:strict-missing-properties DiagnosticGroups/STRICT_MISSING_PROPERTIES
:strict-module-dep-check DiagnosticGroups/STRICT_MODULE_DEP_CHECK
- :strict-requires DiagnosticGroups/STRICT_REQUIRES
:suspicious-code DiagnosticGroups/SUSPICIOUS_CODE
:too-many-type-params DiagnosticGroups/TOO_MANY_TYPE_PARAMS
:tweaks DiagnosticGroups/TWEAKS
@@ -211,7 +210,8 @@
:watch :watch-error-fn :watch-fn :install-deps :process-shim :rename-prefix :rename-prefix-namespace
:closure-variable-map-in :closure-property-map-in :closure-variable-map-out :closure-property-map-out
:stable-names :ignore-js-module-exts :opts-cache :aot-cache :elide-strict :fingerprint :spec-skip-macros
- :nodejs-rt :target-fn :deps-cmd :bundle-cmd :global-goog-object&array :node-modules-dirs})
+ :nodejs-rt :target-fn :deps-cmd :bundle-cmd :global-goog-object&array :node-modules-dirs :lite-mode
+ :elide-to-string})
(def string->charset
{"iso-8859-1" StandardCharsets/ISO_8859_1
@@ -1126,13 +1126,17 @@
(let [requires (set (mapcat deps/-requires inputs))
required-js (js-dependencies opts requires)]
(concat
- (map
- (fn [{:keys [foreign url file provides requires] :as js-map}]
- (let [url (or url (io/resource file))]
- (merge
- (javascript-file foreign url provides requires)
- js-map)))
- required-js)
+ (->> required-js
+ ;; :foreign-libs which declare :external? have no sources (they are included
+ ;; on the page via some script tag we'll never see). :require-global libs are
+ ;; implicit :foreign-libs where :external? is true
+ (remove :external?)
+ (map
+ (fn [{:keys [foreign url file provides requires] :as js-map}]
+ (let [url (or url (io/resource file))]
+ (merge
+ (javascript-file foreign url provides requires)
+ js-map)))))
(when (-> @env/*compiler* :options :emit-constants)
[(constants-javascript-file opts)])
inputs)))
@@ -1389,11 +1393,12 @@
(let [source (first sources)]
(recur
(next sources)
- (let [{:keys [provides source-url]} source]
- (if (and provides source-url)
+ (let [{:keys [provides]} source
+ url (or (:source-url source) (:url source))]
+ (if (and provides url)
(assoc relpaths
- (.getPath ^URL source-url)
- (util/ns->relpath (first provides) (util/ext source-url)))
+ (.getPath ^URL url)
+ (util/ns->relpath (first provides) (util/ext url)))
relpaths))
(if-let [url (:url source)]
(let [path (.getPath ^URL url)]
@@ -1410,19 +1415,19 @@
(spit
(io/file name)
(sm/encode merged
- {:preamble-line-count (+ (:preamble-line-count opts 0)
- (:foreign-deps-line-count opts 0))
- :lines (+ (:lineCount sm-json)
- (:preamble-line-count opts 0)
- (:foreign-deps-line-count opts 0)
- 2)
- :file name
- :output-dir (util/output-directory opts)
- :source-map (:source-map opts)
- :source-map-path (:source-map-path opts)
- :source-map-timestamp (:source-map-timestamp opts)
+ {:preamble-line-count (+ (:preamble-line-count opts 0)
+ (:foreign-deps-line-count opts 0))
+ :lines (+ (:lineCount sm-json)
+ (:preamble-line-count opts 0)
+ (:foreign-deps-line-count opts 0)
+ 2)
+ :file name
+ :output-dir (util/output-directory opts)
+ :source-map (:source-map opts)
+ :source-map-path (:source-map-path opts)
+ :source-map-timestamp (:source-map-timestamp opts)
:source-map-pretty-print (:source-map-pretty-print opts)
- :relpaths relpaths}))))))
+ :relpaths relpaths}))))))
(defn write-variable-maps [^Result result opts]
(let [var-out (:closure-variable-map-out opts)]
@@ -1603,7 +1608,15 @@
"], ["
;; even under Node.js where runtime require is possible
;; this is necessary - see CLJS-2151
- (ns-list (cond->> (deps/-requires input)
+ (ns-list (cond->>
+ ;; remove the global js namespace, it's not real
+ ;; comes from :refer-global
+ ;; remove external? foreign deps - they are already loaded
+ ;; in the environment, there is nothing to do.
+ ;; :require-global is the typical case here
+ (->> (deps/-requires input) ;; returns nses as strings, not symbols
+ (remove #{"js"})
+ (remove ana/external-dep?))
;; under Node.js we emit native `require`s for these
(= :nodejs (:target opts))
(filter (complement ana/node-module-dep?))))
@@ -1960,13 +1973,7 @@
(.toSource closure-compiler ast-root)))))
(defn- sorting-dependency-options []
- (try
- (if (contains? (:flags (clojure.reflect/reflect DependencyOptions)) :abstract)
- (eval '(do
- (import '(com.google.javascript.jscomp DependencyOptions))
- (DependencyOptions/sortOnly)))
- (doto (DependencyOptions.)
- (.setDependencySorting true)))))
+ (DependencyOptions/sortOnly))
(defn convert-js-modules
"Takes a list JavaScript modules as an IJavaScript and rewrites them into a Google
@@ -2516,8 +2523,14 @@
:ups-foreign-libs (expand-libs foreign-libs)
:ups-externs externs
:emit-constants emit-constants
- :cache-analysis-format (:cache-analysis-format opts :transit))
- (update-in [:preamble] #(into (or % []) ["cljs/imul.js"])))
+ :cache-analysis-format (:cache-analysis-format opts :transit)))
+
+ (not (:lite-mode opts))
+ (update-in [:preamble] #(into (or % []) ["cljs/imul.js"]))
+
+ (:lite-mode opts)
+ (assoc-in [:closure-defines (str (comp/munge 'cljs.core/LITE_MODE))]
+ (:lite-mode opts))
(:target opts)
(assoc-in [:closure-defines (str (comp/munge 'cljs.core/*target*))]
diff --git a/src/main/clojure/cljs/compiler.cljc b/src/main/clojure/cljs/compiler.cljc
index b96c09b36b..1fbf54ec20 100644
--- a/src/main/clojure/cljs/compiler.cljc
+++ b/src/main/clojure/cljs/compiler.cljc
@@ -142,7 +142,7 @@
ss (map rf (string/split ss #"\."))
ss (string/join "." ss)
ms #?(:clj (clojure.lang.Compiler/munge ss)
- :cljs (#'cljs.core/munge-str ss))]
+ :cljs (munge-str ss))]
(if (symbol? s)
(symbol ms)
ms)))))
@@ -522,6 +522,27 @@
(and (every? #(= (:op %) :const) keys)
(= (count (into #{} keys)) (count keys)))))
+(defn obj-map-key [x]
+ (if (keyword? x)
+ (str \" "\\uFDD0" \'
+ (if (namespace x)
+ (str (namespace x) "/") "")
+ (name x)
+ \")
+ (str \" x \")))
+
+(defn emit-obj-map [str-keys vals comma-sep distinct-keys?]
+ (if (zero? (count str-keys))
+ (emits "cljs.core.ObjMap.EMPTY")
+ (emits "cljs.core.ObjMap.fromObject([" (comma-sep str-keys) "], {"
+ (comma-sep (map (fn [k v] (str k ":" (emit-str v))) str-keys vals))
+ "})")))
+
+(defn emit-lite-map [keys vals comma-sep distinct-keys?]
+ (if (zero? (count keys))
+ (emits "cljs.core.HashMapLite.EMPTY")
+ (emits "cljs.core.HashMapLite.fromArrays([" (comma-sep keys) "], [" (comma-sep vals) "])")))
+
(defn emit-map [keys vals comma-sep distinct-keys?]
(cond
(zero? (count keys))
@@ -544,9 +565,14 @@
"])")))
(defmethod emit* :map
- [{:keys [env keys vals]}]
+ [{:keys [env form keys vals]}]
(emit-wrap env
- (emit-map keys vals comma-sep distinct-keys?)))
+ (if (ana/lite-mode?)
+ (let [form-keys (clojure.core/keys form)]
+ (if (every? #(or (string? %) (keyword? %)) form-keys)
+ (emit-obj-map (map obj-map-key form-keys) vals comma-sep distinct-keys?)
+ (emit-lite-map keys vals comma-sep distinct-keys?)))
+ (emit-map keys vals comma-sep distinct-keys?))))
(defn emit-list [items comma-sep]
(if (empty? items)
@@ -562,10 +588,17 @@
", 5, cljs.core.PersistentVector.EMPTY_NODE, [" (comma-sep items) "], null)")
(emits "cljs.core.PersistentVector.fromArray([" (comma-sep items) "], true)")))))
+(defn emit-lite-vector [items comma-sep]
+ (if (empty? items)
+ (emits "cljs.core.VectorLite.EMPTY")
+ (emits "new cljs.core.VectorLite(null, [" (comma-sep items) "], null)")))
+
(defmethod emit* :vector
[{:keys [items env]}]
(emit-wrap env
- (emit-vector items comma-sep)))
+ (if (ana/lite-mode?)
+ (emit-lite-vector items comma-sep)
+ (emit-vector items comma-sep))))
(defn distinct-constants? [items]
(let [items (map ana/unwrap-quote items)]
@@ -583,10 +616,17 @@
:else (emits "cljs.core.PersistentHashSet.createAsIfByAssoc([" (comma-sep items) "])")))
+(defn emit-lite-set [items comma-sep distinct-constants?]
+ (if (empty? items)
+ (emits "cljs.core.SetLite.EMPTY")
+ (emits "cljs.core.set_lite([" (comma-sep items) "])")))
+
(defmethod emit* :set
[{:keys [items env]}]
(emit-wrap env
- (emit-set items comma-sep distinct-constants?)))
+ (if (ana/lite-mode?)
+ (emit-lite-set items comma-sep distinct-constants?)
+ (emit-set items comma-sep distinct-constants?))))
(defn emit-js-object [items emit-js-object-val]
(emits "({")
@@ -641,7 +681,8 @@
(defn safe-test? [env e]
(let [tag (ana/infer-tag env e)]
- (or (#{'boolean 'seq} tag) (truthy-constant? e))))
+ (or ('#{boolean seq} (ana/js-prim-ctor->tag tag tag))
+ (truthy-constant? e))))
(defmethod emit* :if
[{:keys [test then else env unchecked]}]
@@ -660,10 +701,16 @@
(emitln then "} else {")
(emitln else "}"))))))
+(defn iife-open [{:keys [async]}]
+ (str (when async "(await ") "(" (when async "async ") "function (){"))
+
+(defn iife-close [{:keys [async]}]
+ (str "})()" (when async ")")))
+
(defmethod emit* :case
[{v :test :keys [nodes default env]}]
(when (= (:context env) :expr)
- (emitln "(function(){"))
+ (emitln (iife-open env)))
(let [gs (gensym "caseval__")]
(when (= :expr (:context env))
(emitln "var " gs ";"))
@@ -682,12 +729,13 @@
(emitln default)))
(emitln "}")
(when (= :expr (:context env))
- (emitln "return " gs ";})()"))))
+ (emitln "return " gs ";"
+ (iife-close env)))))
(defmethod emit* :throw
[{throw :exception :keys [env]}]
(if (= :expr (:context env))
- (emits "(function(){throw " throw "})()")
+ (emits (iife-open env) "throw " throw (iife-close env))
(emitln "throw " throw ";")))
(def base-types
@@ -824,7 +872,7 @@
(when (= :return (:context env))
(emitln "return ("))
(when (:def-emits-var env)
- (emitln "(function (){"))
+ (emitln (iife-open env)))
(emits var)
(when init
(emits " = "
@@ -837,7 +885,8 @@
{:op :the-var
:env (assoc env :context :expr)}
var-ast))
- (emitln ");})()"))
+ (emitln ");"
+ (iife-close env)))
(when (= :return (:context env))
(emitln ")"))
;; NOTE: JavaScriptCore does not like this under advanced compilation
@@ -895,18 +944,19 @@
(defn emit-fn-method
[{expr :body :keys [type name params env recurs]}]
- (emit-wrap env
- (emits "(function " (munge name) "(")
- (emit-fn-params params)
- (emitln "){")
- (when type
- (emitln "var self__ = this;"))
- (when recurs (emitln "while(true){"))
- (emits expr)
- (when recurs
- (emitln "break;")
- (emitln "}"))
- (emits "})")))
+ (let [async (:async env)]
+ (emit-wrap env
+ (emits "(" (when async "async ") "function " (munge name) "(")
+ (emit-fn-params params)
+ (emitln "){")
+ (when type
+ (emitln "var self__ = this;"))
+ (when recurs (emitln "while(true){"))
+ (emits expr)
+ (when recurs
+ (emitln "break;")
+ (emitln "}"))
+ (emits "})"))))
(defn emit-arguments-to-array
"Emit code that copies function arguments into an array starting at an index.
@@ -927,9 +977,10 @@
(emit-wrap env
(let [name (or name (gensym))
mname (munge name)
- delegate-name (str mname "__delegate")]
+ delegate-name (str mname "__delegate")
+ async (:async env)]
(emitln "(function() { ")
- (emits "var " delegate-name " = function (")
+ (emits "var " delegate-name " = " (when async "async ") "function (")
(doseq [param params]
(emit param)
(when-not (= param (last params)) (emits ",")))
@@ -943,10 +994,11 @@
(emitln "}"))
(emitln "};")
- (emitln "var " mname " = function (" (comma-sep
- (if variadic
- (concat (butlast params) ['var_args])
- params)) "){")
+ (emitln "var " mname " = " (when async "async ") "function ("
+ (comma-sep
+ (if variadic
+ (concat (butlast params) ['var_args])
+ params)) "){")
(when type
(emitln "var self__ = this;"))
(when variadic
@@ -983,13 +1035,14 @@
(when (or in-loop (seq recur-params))
(mapcat :params loop-lets)))
(map munge)
- seq)]
+ seq)
+ async (:async env)]
(when loop-locals
(when (= :return (:context env))
(emits "return "))
(emitln "((function (" (comma-sep (map munge loop-locals)) "){")
(when-not (= :return (:context env))
- (emits "return ")))
+ (emits "return ")))
(if (= 1 (count methods))
(if variadic
(emit-variadic-fn-method (assoc (first methods) :name name))
@@ -1013,9 +1066,10 @@
(emit-variadic-fn-method meth)
(emit-fn-method meth))
(emitln ";"))
- (emitln mname " = function(" (comma-sep (if variadic
- (concat (butlast maxparams) ['var_args])
- maxparams)) "){")
+ (emitln mname " = " (when async "async ") "function("
+ (comma-sep (if variadic
+ (concat (butlast maxparams) ['var_args])
+ maxparams)) "){")
(when variadic
(emits "var ")
(emit (last maxparams))
@@ -1060,10 +1114,10 @@
(defmethod emit* :do
[{:keys [statements ret env]}]
(let [context (:context env)]
- (when (and (seq statements) (= :expr context)) (emitln "(function (){"))
+ (when (and (seq statements) (= :expr context)) (emitln (iife-open env)))
(doseq [s statements] (emitln s))
(emit ret)
- (when (and (seq statements) (= :expr context)) (emitln "})()"))))
+ (when (and (seq statements) (= :expr context)) (emitln (iife-close env)))))
(defmethod emit* :try
[{try :body :keys [env catch name finally]}]
@@ -1071,7 +1125,7 @@
(if (or name finally)
(do
(when (= :expr context)
- (emits "(function (){"))
+ (emits (iife-open env)))
(emits "try{" try "}")
(when name
(emits "catch (" (munge name) "){" catch "}"))
@@ -1079,13 +1133,14 @@
(assert (not= :const (:op (ana/unwrap-quote finally))) "finally block cannot contain constant")
(emits "finally {" finally "}"))
(when (= :expr context)
- (emits "})()")))
+ (emits (iife-close env))))
(emits try))))
(defn emit-let
[{expr :body :keys [bindings env]} is-loop]
(let [context (:context env)]
- (when (= :expr context) (emits "(function (){"))
+ (when (= :expr context)
+ (emits (iife-open env)))
(binding [*lexical-renames*
(into *lexical-renames*
(when (= :statement context)
@@ -1104,7 +1159,7 @@
(when is-loop
(emitln "break;")
(emitln "}")))
- (when (= :expr context) (emits "})()"))))
+ (when (= :expr context) (emits (iife-close env)))))
(defmethod emit* :let [ast]
(emit-let ast false))
@@ -1125,11 +1180,11 @@
(defmethod emit* :letfn
[{expr :body :keys [bindings env]}]
(let [context (:context env)]
- (when (= :expr context) (emits "(function (){"))
+ (when (= :expr context) (emits (iife-open env)))
(doseq [{:keys [init] :as binding} bindings]
(emitln "var " (munge binding) " = " init ";"))
(emits expr)
- (when (= :expr context) (emits "})()"))))
+ (when (= :expr context) (emits (iife-close env)))))
(defn protocol-prefix [psym]
(symbol (str (-> (str psym)
@@ -1260,6 +1315,14 @@
(comma-sep args)
"))")))
+(defmethod emit* :qualified-method
+ [{ctor :class :keys [args env kind name]}]
+ (if (= :new kind)
+ (emit-wrap env
+ (emits "(function (...args) { return Reflect.construct(" ctor ", args) })"))
+ (emit-wrap env
+ (emits "(function (x, ...args) { return Reflect.apply(" ctor ".prototype." name ", x, args) })"))))
+
(defmethod emit* :set!
[{:keys [target val env]}]
(emit-wrap env (emits "(" target " = " val ")")))
@@ -1271,19 +1334,24 @@
(apply str
(map #(str "['" % "']") xs)))))
-(defn emit-global-export [ns-name global-exports lib]
- (let [[lib' sublib] (ana/lib&sublib lib)]
+(defn emit-global-export [ns-name global-exports lib opts]
+ (let [[lib' sublib] (ana/lib&sublib lib)
+ ref (str "goog.global"
+ ;; Convert object dot access to bracket access
+ (->> (string/split (name (or (get global-exports (symbol lib'))
+ (get global-exports (name lib'))))
+ #"\.")
+ (map (fn [prop] (str "[\"" prop "\"]")))
+ (apply str)))]
+ (when (and (ana/external-dep? lib')
+ (= :none (:optimizations opts)))
+ (emitln
+ "if(!" ref ") throw new Error(\"External library, " lib' ", never provided\");"))
(emitln
(munge ns-name) "."
(ana/munge-global-export lib)
- " = goog.global"
- ;; Convert object dot access to bracket access
- (->> (string/split (name (or (get global-exports (symbol lib'))
- (get global-exports (name lib'))))
- #"\.")
- (map (fn [prop]
- (str "[\"" prop "\"]")))
- (apply str))
+ " = "
+ ref
(sublib-select sublib)
";")))
@@ -1326,7 +1394,10 @@
escape-string
wrap-in-double-quotes)
");"))
- (emitln "goog.require('" (munge lib) "');"))))]
+ (if-not (ana/external-dep? lib)
+ (emitln "goog.require('" (munge lib) "');")
+ ;; TODO: validate the lib exists
+ ))))]
:cljs
[(and (ana/foreign-dep? lib)
(not (keyword-identical? optimizations :none)))
@@ -1365,7 +1436,7 @@
;; Global Exports
(doseq [lib global-exports-libs]
(let [{:keys [global-exports]} (get js-dependency-index (name (-> lib ana/lib&sublib first)))]
- (emit-global-export ns-name global-exports lib)))
+ (emit-global-export ns-name global-exports lib options)))
(when (-> libs meta :reload-all)
(emitln "if(!COMPILED) " loaded-libs " = cljs.core.into(" loaded-libs-temp ", " loaded-libs ");"))))
diff --git a/src/main/clojure/cljs/core.cljc b/src/main/clojure/cljs/core.cljc
index 72f4654270..d8a2d0fd59 100644
--- a/src/main/clojure/cljs/core.cljc
+++ b/src/main/clojure/cljs/core.cljc
@@ -7,12 +7,12 @@
; You must not remove this notice, or any other, from this software.
(ns cljs.core
- (:refer-clojure :exclude [-> ->> .. amap and areduce alength aclone assert assert-args binding bound-fn case comment
+ (:refer-clojure :exclude [-> ->> .. amap and areduce alength aclone assert await binding bound-fn case comment
cond condp declare definline definterface defmethod defmulti defn defn- defonce
defprotocol defrecord defstruct deftype delay destructure doseq dosync dotimes doto
extend-protocol extend-type fn for future gen-class gen-interface
if-let if-not import io! lazy-cat lazy-seq let letfn locking loop
- memfn ns or proxy proxy-super pvalues refer-clojure reify sync time
+ memfn ns or proxy proxy-super pvalues reify sync time
when when-first when-let when-not while with-bindings with-in-str
with-loading-context with-local-vars with-open with-out-str with-precision with-redefs
satisfies? identical? true? false? number? nil? instance? symbol? keyword? string? str get
@@ -246,28 +246,26 @@
[p & specs]
(emit-extend-protocol p specs)))
-#?(:cljs
- (core/defn ^{:private true}
- maybe-destructured
- [params body]
- (if (every? core/symbol? params)
- (cons params body)
- (core/loop [params params
- new-params (with-meta [] (meta params))
- lets []]
- (if params
- (if (core/symbol? (first params))
- (recur (next params) (conj new-params (first params)) lets)
- (core/let [gparam (gensym "p__")]
- (recur (next params) (conj new-params gparam)
- (core/-> lets (conj (first params)) (conj gparam)))))
- `(~new-params
- (let ~lets
- ~@body)))))))
-
-#?(:cljs
- (core/defmacro fn
- "params => positional-params* , or positional-params* & rest-param
+(core/defn ^{:private true}
+ maybe-destructured
+ [params body]
+ (if (every? core/symbol? params)
+ (cons params body)
+ (core/loop [params params
+ new-params (with-meta [] (meta params))
+ lets []]
+ (if params
+ (if (core/symbol? (first params))
+ (recur (next params) (conj new-params (first params)) lets)
+ (core/let [gparam (gensym "p__")]
+ (recur (next params) (conj new-params gparam)
+ (core/-> lets (conj (first params)) (conj gparam)))))
+ `(~new-params
+ (let ~lets
+ ~@body))))))
+
+(core/defmacro fn
+ "params => positional-params* , or positional-params* & rest-param
positional-param => binding-form
rest-param => binding-form
binding-form => name, or destructuring-form
@@ -275,35 +273,35 @@
Defines a function
See https://clojure.org/reference/special_forms#fn for more information"
- {:forms '[(fn name? [params*] exprs*) (fn name? ([params*] exprs*) +)]}
- [& sigs]
- (core/let [name (if (core/symbol? (first sigs)) (first sigs) nil)
- sigs (if name (next sigs) sigs)
- sigs (if (vector? (first sigs))
- (core/list sigs)
- (if (seq? (first sigs))
- sigs
- ;; Assume single arity syntax
- (throw (js/Error.
- (if (seq sigs)
- (core/str "Parameter declaration "
- (core/first sigs)
- " should be a vector")
- (core/str "Parameter declaration missing"))))))
- psig (fn* [sig]
+ {:forms '[(fn name? [params*] exprs*) (fn name? ([params*] exprs*) +)]}
+ [& sigs]
+ (core/let [name (if (core/symbol? (first sigs)) (first sigs) nil)
+ sigs (if name (next sigs) sigs)
+ sigs (if (vector? (first sigs))
+ (core/list sigs)
+ (if (seq? (first sigs))
+ sigs
+ ;; Assume single arity syntax
+ (throw (#?(:clj Exception. :cljs js/Error.)
+ (if (seq sigs)
+ (core/str "Parameter declaration "
+ (core/first sigs)
+ " should be a vector")
+ (core/str "Parameter declaration missing"))))))
+ psig (fn* [sig]
;; Ensure correct type before destructuring sig
(core/when (not (seq? sig))
- (throw (js/Error.
- (core/str "Invalid signature " sig
- " should be a list"))))
+ (throw (#?(:clj Exception. :cljs js/Error.)
+ (core/str "Invalid signature " sig
+ " should be a list"))))
(core/let [[params & body] sig
_ (core/when (not (vector? params))
- (throw (js/Error.
- (if (seq? (first sigs))
- (core/str "Parameter declaration " params
- " should be a vector")
- (core/str "Invalid signature " sig
- " should be a list")))))
+ (throw (#?(:clj Exception. :cljs js/Error.)
+ (if (seq? (first sigs))
+ (core/str "Parameter declaration " params
+ " should be a vector")
+ (core/str "Invalid signature " sig
+ " should be a list")))))
conds (core/when (core/and (next body) (map? (first body)))
(first body))
body (if conds (next body) body)
@@ -319,15 +317,17 @@
body)
body (if pre
(concat (map (fn* [c] `(assert ~c)) pre)
- body)
+ body)
body)]
(maybe-destructured params body)))
- new-sigs (map psig sigs)]
- (with-meta
- (if name
- (list* 'fn* name new-sigs)
- (cons 'fn* new-sigs))
- (meta &form)))))
+ new-sigs (map psig sigs)
+ fn-sym-meta (meta (first &form))
+ fn*-sym (with-meta 'fn* fn-sym-meta)]
+ (with-meta
+ (if name
+ (list* fn*-sym name new-sigs)
+ (cons fn*-sym new-sigs))
+ (meta &form))))
#?(:cljs
(core/defmacro defn-
@@ -849,23 +849,45 @@
(core/defn- string-expr [e]
(vary-meta e assoc :tag 'string))
-(core/defmacro str
+(core/defmacro str_
([] "")
([x]
(if (typed-expr? &env x '#{string})
x
- (string-expr (core/list 'js* "cljs.core.str.cljs$core$IFn$_invoke$arity$1(~{})" x))))
+ (string-expr (core/list 'js* "cljs.core.str_(~{})" x))))
([x & ys]
(core/let [interpolate (core/fn [x]
(if (typed-expr? &env x '#{string clj-nil})
"~{}"
- "cljs.core.str.cljs$core$IFn$_invoke$arity$1(~{})"))
+ "cljs.core.str_(~{})"))
strs (core/->> (core/list* x ys)
(map interpolate)
(interpose ",")
(apply core/str))]
(string-expr (list* 'js* (core/str "[" strs "].join('')") x ys)))))
+(core/defn- compile-time-constant? [x]
+ (core/or
+ (core/string? x)
+ (core/keyword? x)
+ (core/boolean? x)
+ (core/number? x)))
+
+;; TODO: should probably be a compiler pass to avoid the code duplication
+(core/defmacro str
+ [& xs]
+ (core/let [interpolate (core/fn [x]
+ (core/cond
+ (compile-time-constant? x)
+ ["+~{}" x]
+ :else
+ ;; Note: can't assume non-nil despite tag here, so we go through str 1-arity
+ ["+cljs.core.str.cljs$core$IFn$_invoke$arity$1(~{})" x]))
+ strs+args (keep interpolate xs)
+ strs (string/join (map first strs+args))
+ args (map second strs+args)]
+ (string-expr (list* 'js* (core/str "(\"\"" strs ")") args))))
+
(core/defn- bool-expr [e]
(vary-meta e assoc :tag 'boolean))
@@ -950,6 +972,10 @@
(reduce core/str ""))
" */\n"))))
+(core/defmacro await [expr]
+ (core/assert (:async &env) "await can only be used in async contexts")
+ (core/list 'js* "(await ~{})" expr))
+
(core/defmacro unsafe-cast
"EXPERIMENTAL: Subject to change. Unsafely cast a value to a different type."
[t x]
@@ -1177,17 +1203,21 @@
(core/defmacro ^::ana/numeric neg? [x]
`(< ~x 0))
-(core/defmacro ^::ana/numeric max
+(core/defmacro ^::ana/numeric unchecked-max
([x] x)
- ([x y] `(let [x# ~x, y# ~y]
- (~'js* "((~{} > ~{}) ? ~{} : ~{})" x# y# x# y#)))
- ([x y & more] `(max (max ~x ~y) ~@more)))
+ ([x y]
+ `(let [x# ~x, y# ~y]
+ (if (> x# y#) x# y#)))
+ ([x y & more]
+ `(max (max ~x ~y) ~@more)))
-(core/defmacro ^::ana/numeric min
+(core/defmacro ^::ana/numeric unchecked-min
([x] x)
- ([x y] `(let [x# ~x, y# ~y]
- (~'js* "((~{} < ~{}) ? ~{} : ~{})" x# y# x# y#)))
- ([x y & more] `(min (min ~x ~y) ~@more)))
+ ([x y]
+ `(let [x# ~x, y# ~y]
+ (if (< x# y#) x# y#)))
+ ([x y & more]
+ `(min (min ~x ~y) ~@more)))
(core/defmacro ^::ana/numeric js-mod [num div]
(core/list 'js* "(~{} % ~{})" num div))
@@ -1208,8 +1238,9 @@
([x y] (core/list 'js* "(~{} | ~{})" x y))
([x y & more] `(bit-or (bit-or ~x ~y) ~@more)))
-(core/defmacro ^::ana/numeric int [x]
- `(bit-or ~x 0))
+(core/defmacro int
+ [x]
+ (core/list 'js* "(~{} | 0)" x))
(core/defmacro ^::ana/numeric bit-xor
([x y] (core/list 'js* "(~{} ^ ~{})" x y))
@@ -1365,7 +1396,7 @@
[& impls]
(core/let [t (with-meta
(gensym
- (core/str "t_"
+ (core/str "t_reify_"
(string/replace (core/str (munge ana/*cljs-ns*)) "." "$")))
{:anonymous true})
meta-sym (gensym "meta")
@@ -1382,7 +1413,11 @@
IMeta
(~'-meta [~this-sym] ~meta-sym)
~@impls))
- (new ~t ~@locals ~(ana/elide-reader-meta (meta &form))))))
+ (new ~t ~@locals
+ ;; if the form meta is empty, emit nil
+ ~(core/let [form-meta (ana/elide-reader-meta (meta &form))]
+ (core/when-not (empty? form-meta)
+ form-meta))))))
(core/defmacro specify!
"Identical to reify but mutates its first argument."
@@ -1485,13 +1520,18 @@
~@body))))
(core/defn- add-obj-methods [type type-sym sigs]
- (map (core/fn [[f & meths :as form]]
- (core/let [[f meths] (if (vector? (first meths))
- [f [(rest form)]]
- [f meths])]
- `(set! ~(extend-prefix type-sym f)
- ~(with-meta `(fn ~@(map #(adapt-obj-params type %) meths)) (meta form)))))
- sigs))
+ (core/->> sigs
+ ;; Elide all toString methods in :lite-mode
+ (remove
+ (core/fn [[f]]
+ (core/and (ana/elide-to-string?) (core/= 'toString f))))
+ (map
+ (core/fn [[f & meths :as form]]
+ (core/let [[f meths] (if (vector? (first meths))
+ [f [(rest form)]]
+ [f meths])]
+ `(set! ~(extend-prefix type-sym f)
+ ~(with-meta `(fn ~@(map #(adapt-obj-params type %) meths)) (meta form))))))))
(core/defn- ifn-invoke-methods [type type-sym [f & meths :as form]]
(map
@@ -1789,17 +1829,22 @@
[t fields & impls]
(validate-fields "deftype" t fields)
(core/let [env &env
- r (:name (cljs.analyzer/resolve-var (dissoc env :locals) t))
+ v (cljs.analyzer/resolve-var (dissoc env :locals) t)
+ r (:name v)
[fpps pmasks] (prepare-protocol-masks env impls)
protocols (collect-protocols impls env)
t (vary-meta t assoc
:protocols protocols
- :skip-protocol-flag fpps) ]
+ :skip-protocol-flag fpps)]
`(do
(deftype* ~t ~fields ~pmasks
~(if (seq impls)
`(extend-type ~t ~@(dt->et t impls fields))))
- (set! (.-getBasis ~t) (fn [] '[~@fields]))
+ ;; don't emit static basis method w/ reify
+ ;; nor for core types
+ ~@(core/when-not (core/or (string/starts-with? (name t) "t_reify")
+ (= 'cljs.core (:ns v)))
+ [`(set! (.-getBasis ~t) (fn [] '[~@fields]))])
(set! (.-cljs$lang$type ~t) true)
(set! (.-cljs$lang$ctorStr ~t) ~(core/str r))
(set! (.-cljs$lang$ctorPrWriter ~t) (fn [this# writer# opt#] (-write writer# ~(core/str r))))
@@ -3089,6 +3134,20 @@
[& args]
`(~'ns* ~(cons :refer-clojure args)))
+(core/defmacro refer-global
+ "Refer global js vars. Supports renaming via :rename.
+
+ (refer-global :only '[Date Symbol] :rename '{Symbol Sym})"
+ [& args]
+ `(~'ns* ~(cons :refer-global args)))
+
+(core/defmacro require-global
+ "Require libraries in the global JS environment.
+
+ (require-global '[SomeLib :as lib :refer [foo]])"
+ [& args]
+ `(~'ns* ~(cons :require-global args)))
+
;; INTERNAL - do not use, only for Node.js
(core/defmacro load-file* [f]
`(goog/nodeGlobalRequire ~f))
@@ -3154,7 +3213,7 @@
(. self# (~(get-delegate) (seq ~restarg))))))))]
`(do
(set! (. ~sym ~(get-delegate-prop))
- (fn (~(vec sig) ~@body)))
+ (~(with-meta `fn (meta sym)) (~(vec sig) ~@body)))
~@(core/when solo
`[(set! (. ~sym ~'-cljs$lang$maxFixedArity)
~(core/dec (count sig)))])
@@ -3235,7 +3294,7 @@
{:variadic? false :fixed-arity (count sig)})
~(symbol (core/str "-cljs$core$IFn$_invoke$arity$"
(count sig))))
- (fn ~method))))]
+ (~(with-meta `fn (core/meta name)) ~method))))]
(core/let [rname (symbol (core/str ana/*cljs-ns*) (core/str name))
arglists (map first fdecl)
macro? (:macro meta)
@@ -3287,9 +3346,9 @@
argseq#))))
(if (:macro meta)
`(throw (js/Error.
- (str "Invalid arity: " (- (alength (js-arguments)) 2))))
+ (.join (array "Invalid arity: " (- (alength (js-arguments)) 2)) "")))
`(throw (js/Error.
- (str "Invalid arity: " (alength (js-arguments))))))))))
+ (.join (array "Invalid arity: " (alength (js-arguments))) ""))))))))
~@(map #(fn-method name %) fdecl)
;; optimization properties
(set! (. ~name ~'-cljs$lang$maxFixedArity) ~maxfa)
diff --git a/src/main/clojure/cljs/externs.clj b/src/main/clojure/cljs/externs.clj
index c5343e1b17..e354aac745 100644
--- a/src/main/clojure/cljs/externs.clj
+++ b/src/main/clojure/cljs/externs.clj
@@ -15,7 +15,7 @@
CompilerOptions CompilerOptions$Environment SourceFile CompilerInput CommandLineRunner]
[com.google.javascript.jscomp.parsing Config$JsDocParsing]
[com.google.javascript.rhino
- Node Token JSTypeExpression JSDocInfo$Visibility]
+ Node Token JSTypeExpression JSDocInfo JSDocInfo$Visibility]
[java.util.logging Level]
[java.net URL]))
@@ -61,12 +61,23 @@
(and (= type :string-lit)
(= "undefined" value)))
+(defn add-prefix
+ "Externs inference uses :prefix meta to both resolve externs as well as generate
+ missing externs information. Google Closure Compiler default externs includes
+ nested types like webCrypto.Crypto. Add prefix information to the returned symbol to
+ simplify resolution later."
+ [type-str]
+ (with-meta (symbol type-str)
+ {:prefix (->> (string/split (name type-str) #"\.")
+ (map symbol) vec)}))
+
(defn simplify-texpr
[texpr]
(case (:type texpr)
- :string-lit (some-> (:value texpr) symbol)
- (:star :qmark) 'any
- :bang (simplify-texpr (-> texpr :children first))
+ :string-lit (-> texpr :value add-prefix)
+ :star 'any
+ ;; TODO: qmark should probably be #{nil T}
+ (:qmark :bang) (simplify-texpr (-> texpr :children first))
:pipe (let [[x y] (:children texpr)]
(if (undefined? y)
(simplify-texpr x)
@@ -77,14 +88,13 @@
(some-> (.getRoot texpr) parse-texpr simplify-texpr))
(defn params->method-params [xs]
- (letfn [(not-opt? [x]
- (not (string/starts-with? (name x) "opt_")))]
- (let [required (into [] (take-while not-opt? xs))
- opts (drop-while not-opt? xs)]
- (loop [ret [required] opts opts]
- (if-let [opt (first opts)]
- (recur (conj ret (conj (last ret) opt)) (drop 1 opts))
- (seq ret))))))
+ (let [not-opt? (complement :optional?)
+ required (into [] (map :name (take-while not-opt? xs)))
+ opts (map :name (drop-while not-opt? xs))]
+ (loop [ret [required] opts opts]
+ (if-let [opt (first opts)]
+ (recur (conj ret (conj (last ret) opt)) (drop 1 opts))
+ (seq ret)))))
(defn generic? [t]
(let [s (name t)]
@@ -97,6 +107,18 @@
(= t 'Array) 'array
:else t)))
+(defn get-params
+ "Return param information in JSDoc appearance order. GCL is relatively
+ civilized, so this isn't really a problem."
+ [^JSDocInfo info]
+ (map
+ (fn [n]
+ (let [t (.getParameterType info n)]
+ {:name (symbol n)
+ :optional? (.isOptionalArg t)
+ :var-args? (.isVarArgs t)}))
+ (.getParameterNames info)))
+
(defn get-var-info [^Node node]
(when node
(let [info (.getJSDocInfo node)]
@@ -113,15 +135,15 @@
(if (or (.hasReturnType info)
(as-> (.getParameterCount info) c
(and c (pos? c))))
- (let [arglist (into [] (map symbol (.getParameterNames info)))
+ (let [arglist (get-params info)
arglists (params->method-params arglist)]
{:tag 'Function
:js-fn-var true
:ret-tag (or (some-> (.getReturnType info)
get-tag gtype->cljs-type)
'clj-nil)
- :variadic? (boolean (some '#{var_args} arglist))
- :max-fixed-arity (count (take-while #(not= 'var_args %) arglist))
+ :variadic? (boolean (some :var-args? arglist))
+ :max-fixed-arity (count (take-while (complement :var-args?) arglist))
:method-params arglists
:arglists arglists}))))
{:file *source-file*
diff --git a/src/main/clojure/cljs/repl.cljc b/src/main/clojure/cljs/repl.cljc
index b5c53738a1..c588b4fbff 100644
--- a/src/main/clojure/cljs/repl.cljc
+++ b/src/main/clojure/cljs/repl.cljc
@@ -256,7 +256,12 @@
([repl-env requires]
(load-dependencies repl-env requires nil))
([repl-env requires opts]
- (doall (mapcat #(load-namespace repl-env % opts) (distinct requires)))))
+ (->> requires
+ distinct
+ (remove ana/global-ns?)
+ (remove ana/external-dep?)
+ (mapcat #(load-namespace repl-env % opts))
+ doall)))
(defn ^File js-src->cljs-src
"Map a JavaScript output file back to the original ClojureScript source
@@ -604,7 +609,9 @@
env/*compiler*)
(cljsc/compile src
(assoc opts
- :output-file (cljsc/src-file->target-file src)
+ ;; need to set opts to nil here so that we don't
+ ;; double up output-dir
+ :output-file (cljsc/src-file->target-file src nil)
:force true
:mode :interactive)))]
;; copy over the original source file if source maps enabled
@@ -652,7 +659,7 @@
(defn- wrap-fn [form]
(cond
(and (seq? form)
- (#{'ns 'require 'require-macros
+ (#{'ns 'require 'require-macros 'refer-global 'require-global
'use 'use-macros 'import 'refer-clojure} (first form)))
identity
@@ -673,7 +680,7 @@
(defn- init-wrap-fn [form]
(cond
(and (seq? form)
- (#{'ns 'require 'require-macros
+ (#{'ns 'require 'require-macros 'refer-global
'use 'use-macros 'import 'refer-clojure} (first form)))
identity
@@ -1454,8 +1461,14 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}})
(ana-api/resolve &env name)
`(cljs.repl/print-doc
(quote ~(let [var (ana-api/resolve &env name)
- m (select-keys var
- [:ns :name :doc :forms :arglists :macro :url])]
+ ns (-> var :name namespace)
+ m (cond-> var
+ (= "js" ns)
+ (-> :name ana-api/resolve-extern
+ (select-keys [:doc :arglists])
+ (merge {:name name}))
+ (not= "js" ns)
+ (select-keys [:ns :name :doc :forms :arglists :macro :url]))]
(cond-> (update-in m [:name] clojure.core/name)
(:protocol-symbol var)
(assoc :protocol true
diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj
index cf4a345234..9b333127e4 100644
--- a/src/main/clojure/cljs/repl/browser.clj
+++ b/src/main/clojure/cljs/repl/browser.clj
@@ -182,7 +182,7 @@
(defn send-static
[{path :path :as request} conn
- {:keys [static-dir output-dir host port gzip?] :or {output-dir "out"} :as opts}]
+ {:keys [static-dir output-dir host port gzip? compiler-opts] :or {output-dir "out"} :as opts}]
(let [output-dir (when-not (.isAbsolute (io/file output-dir)) output-dir)]
(if (and static-dir (not= "/favicon.ico" path))
(let [path (if (= "/" path) "/index.html" path)
@@ -224,7 +224,11 @@
clojure.browser.repl/PORT ~port}
(merge (:closure-defines @browser-state))
cljsc/normalize-closure-defines
- json/write-str)]
+ json/write-str)
+ preloads (when-let [preloads (:preloads compiler-opts)]
+ (mapv (fn [ns-symb]
+ (str "document.write('');"))
+ preloads))]
(server/send-and-close conn 200
(str "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n"
"var CLOSURE_NO_DEPS = true;\n"
@@ -233,6 +237,7 @@
(when (.exists (io/file output-dir "cljs_deps.js"))
(str "document.write('');\n"))
"document.write('');\n"
+ (when preloads (str/join "\n" preloads))
"document.write('');\n")
"text/javascript" "UTF-8"))
@@ -349,21 +354,27 @@
(defn- waiting-to-connect-message [url]
(print-str "Waiting for browser to connect to" url "..."))
-(defn- maybe-browse-url [base-url]
- (try
- (browse/browse-url (str base-url "?rel=" (System/currentTimeMillis)))
- (catch Throwable t
- (if-some [error-message (not-empty (.getMessage t))]
- (println "Failed to launch a browser:\n" error-message "\n")
- (println "Could not launch a browser.\n"))
- (println "You can instead launch a non-browser REPL (Node or Nashorn).\n")
- (println "You can disable automatic browser launch with this REPL option")
- (println " :launch-browser false")
- (println "and you can specify the listen IP address with this REPL option")
- (println " :host \"127.0.0.1\"\n")
- (println (waiting-to-connect-message base-url)))))
-
-(defn setup [{:keys [working-dir launch-browser server-state] :as repl-env} {:keys [output-dir] :as opts}]
+(defn- maybe-browse-url
+ ([base-url]
+ (maybe-browse-url base-url false))
+ ([base-url new-window]
+ (try
+ (browse/browse-url
+ (cond-> base-url
+ new-window (str "?rel=" (System/currentTimeMillis))))
+ (catch Throwable t
+ (if-some [error-message (not-empty (.getMessage t))]
+ (println "Failed to launch a browser:\n" error-message "\n")
+ (println "Could not launch a browser.\n"))
+ (println "You can instead launch a non-browser REPL (Node or Nashorn).\n")
+ (println "You can disable automatic browser launch with this REPL option")
+ (println " :launch-browser false")
+ (println "and you can specify the listen IP address with this REPL option")
+ (println " :host \"127.0.0.1\"\n")
+ (println (waiting-to-connect-message base-url)))))
+)
+
+(defn setup [{:keys [working-dir launch-browser new-window server-state] :as repl-env} {:keys [output-dir] :as opts}]
(locking lock
(when-not (:socket @server-state)
(binding [browser-state (:browser-state repl-env)
@@ -391,7 +402,7 @@
(server/start repl-env)
(let [base-url (str "http://" (:host repl-env) ":" (:port repl-env))]
(if launch-browser
- (maybe-browse-url base-url)
+ (maybe-browse-url base-url new-window)
(println (waiting-to-connect-message base-url)))))))
(.put outs (thread-name) *out*)
(swap! server-state update :listeners inc))
@@ -458,8 +469,9 @@
{:host host
:port port
:launch-browser true
+ :new-window false
:working-dir (->> [".repl" (util/clojurescript-version)]
- (remove empty?) (string/join "-"))
+ (remove empty?) (string/join "-"))
:static-dir (cond-> ["." "out/"] output-dir (conj output-dir))
:preloaded-libs []
:src "src/"
diff --git a/src/test/cljs/cljs/async_await_test.cljs b/src/test/cljs/cljs/async_await_test.cljs
new file mode 100644
index 0000000000..c3d9d447a3
--- /dev/null
+++ b/src/test/cljs/cljs/async_await_test.cljs
@@ -0,0 +1,276 @@
+(ns cljs.async-await-test
+ (:refer-global :only [Date Promise])
+ (:require [clojure.test :refer [deftest is async]]
+ [cljs.core :as cc :refer [await] :rename {await aw}]
+ [goog.object :as gobj])
+ (:require-macros [cljs.macro-test.macros :refer [await!] :as macros]))
+
+(defn ^:async foo [n]
+ (let [x (await (js/Promise.resolve 10))
+ y (let [y (await (js/Promise.resolve 20))]
+ (inc y))
+ ;; not async
+ f (fn [] 20)]
+ (+ n x y (f))))
+
+(deftest defn-test
+ (async done
+ (try
+ (let [v (await (foo 10))]
+ (is (= 61 v)))
+ (let [v (await (apply foo [10]))]
+ (is (= 61 v)))
+ (catch :default _ (is false))
+ (finally (done)))))
+
+(defn ^:async variadic-foo [n & ns]
+ (let [x (await (js/Promise.resolve n))
+ y (let [y (await (js/Promise.resolve (apply + ns)))]
+ (inc y))
+ ;; not async
+ f (fn [] 20)]
+ (+ n x y (f))))
+
+(deftest variadic-defn-test
+ (async done
+ (try
+ (let [v (await (variadic-foo 10))]
+ (is (= 41 v)))
+ (let [v (await (variadic-foo 10 1 2 3))]
+ (is (= 47 v)))
+ (let [v (await (apply variadic-foo [10 1 2 3]))]
+ (is (= 47 v)))
+ (catch :default _ (is false))
+ (finally (done)))))
+
+(defn ^:async multi-arity-foo
+ ([n] (await n))
+ ([n x] (+ (await n) x)))
+
+(deftest multi-arity-defn-test
+ (async done
+ (try
+ (let [v (await (multi-arity-foo 10))]
+ (is (= 10 v)))
+ (let [v (await (multi-arity-foo 10 20))]
+ (is (= 30 v)))
+ (let [v (await (apply multi-arity-foo [10]))]
+ (is (= 10 v)))
+ (let [v (await (apply multi-arity-foo [10 20]))]
+ (is (= 30 v)))
+ (catch :default _ (is false))
+ (finally (done)))))
+
+(defn ^:async multi-arity-variadic-foo
+ ([n] (await n))
+ ([n & xs] (apply + (await n) xs)))
+
+(deftest multi-arity-variadic-test
+ (async done
+ (try
+ (let [v (await (multi-arity-variadic-foo 10))]
+ (is (= 10 v)))
+ (let [v (await (multi-arity-variadic-foo 10 20))]
+ (is (= 30 v)))
+ (let [v (await (apply multi-arity-variadic-foo [10]))]
+ (is (= 10 v)))
+ (let [v (await (apply multi-arity-variadic-foo [10 20]))]
+ (is (= 30 v)))
+ (catch :default _ (is false))
+ (finally (done)))))
+
+(deftest fn-test
+ (async done
+ (try
+ (let [f (^:async fn [x] (+ x (await (js/Promise.resolve 20))))
+ v (await (f 10))
+ v2 (await (apply f [10]))]
+ (is (= 30 v v2)))
+ (catch :default _ (is false))
+ (finally (done)))))
+
+(deftest varargs-fn-test
+ (async done
+ (try
+ (let [f (^:async fn [x & xs] (apply + x (await (js/Promise.resolve 20)) xs))
+ v (await (f 10))
+ v2 (await (apply f [10]))
+ v3 (await (f 5 5))
+ v4 (await (apply f [5 5]))]
+ (is (= 30 v v2 v3 v4)))
+ (catch :default _ (is false))
+ (finally (done)))))
+
+(deftest variadic-fn-test
+ (async done
+ (try (let [f (^:async fn
+ ([x] (await (js/Promise.resolve x)))
+ ([x y] (cons (await (js/Promise.resolve x)) [y])))]
+ (is (= [1 1 [1 2] [1 2]]
+ [(await (f 1))
+ (await (apply f [1]))
+ (await (f 1 2))
+ (await (apply f [1 2]))])))
+ (catch :default _ (is false))
+ (finally (done)))))
+
+(deftest variadic-varargs-fn-test
+ (async done
+ (try (let [f (^:async fn
+ ([x] (await (js/Promise.resolve x)))
+ ([x & xs] (cons (await (js/Promise.resolve x)) xs)))]
+ (is (= [1 1 [1 2 3] [1 2 3]]
+ [(await (f 1))
+ (await (apply f [1]))
+ (await (f 1 2 3))
+ (await (apply f [1 2 3]))])))
+ (catch :default _ (is false))
+ (finally (done)))))
+
+(deftest await-in-throw-test
+ (async done
+ (let [f (^:async fn [x] (inc (if (odd? x) (throw (await (js/Promise.resolve "dude"))) x)))]
+ (try
+ (let [x (await (f 2))]
+ (is (= 3 x)))
+ (let [x (try (await (f 1))
+ (catch :default e e))]
+ (is (= "dude" x)))
+ (catch :default _ (is false))
+ (finally (done))))))
+
+(deftest await-in-do-test
+ (async done
+ (try
+ (let [a (atom 0)
+ f (^:async fn [] (let [_ (do (swap! a inc)
+ (swap! a + (await (js/Promise.resolve 2))))]
+ @a))
+ v (await (f))]
+ (is (= 3 v)))
+ (catch :default _ (is false))
+ (finally (done)))))
+
+(deftest await-let-fn-test
+ (async done
+ (try
+ (let [f (^:async fn [] (let [v
+ ;; force letfn in expr position
+ (letfn [(^:async f [] (inc (await (js/Promise.resolve 10))))]
+ (inc (await (f))))]
+ (identity v)))
+ v (await (f))]
+ (is (= 12 v)))
+ (catch :default _ (is false))
+ (finally (done)))))
+
+(deftest await-in-loop-test
+ (async done
+ (try
+ (let [f (^:async fn [] (let [x
+ ;; force loop in expr position
+ (loop [xs (map #(js/Promise.resolve %) [1 2 3])
+ ys []]
+ (if (seq xs)
+ (let [x (first xs)
+ v (await x)]
+ (recur (rest xs) (conj ys v)))
+ ys))]
+ (identity x)))
+ v (await (f))]
+ (is (= [1 2 3] v)))
+ (catch :default _ (is false))
+ (finally (done)))))
+
+(deftest await-in-nested
+ (async done
+ (try
+ (let [f (^:async fn []
+ (let [b1 1
+ b2 (let [x 2]
+ (+ x
+ ;; outer let doesn't have awaits
+ ;; but inner let does, so outer let should become async
+ (let [x (await (js/Promise.resolve 1))] x)))
+ b3 (case :foo :foo (case :foo :foo (await (js/Promise.resolve 1))))
+ b4 (int ;; wrapped in int to avoid false positive warning:
+ ;; all arguments must be numbers, got [number
+ ;; ignore] instead
+ (try (throw (throw (await (js/Promise.resolve 1)))) (catch :default _ 1 )))
+ a (atom 0)
+ b5 (do (swap! a inc) (swap! a inc)
+ ;; do with single expr, wrapped in identity to avoid merging with upper do
+ (identity (do (swap! a (await (js/Promise.resolve inc)))))
+ ;; do with multiple exprs, wrapped identity to avoid merging with upper do
+ (identity (do (swap! a inc) (swap! a (await (js/Promise.resolve inc)))))
+ @a)
+ b6 (try (identity (try 1 (finally (await nil))))
+ (finally nil))
+ b7 (letfn [(f [x] x)]
+ (f (letfn [(f [x] x)]
+ (f (await 1)))))]
+ (await (+ b1 b2 b3 b4 b5 b6 b7))))]
+ (is (= 13 (await (f)))))
+ (catch :default _ (is false))
+ (finally (done)))))
+
+(deftest await-with-aliases-or-renamed-and-via-macros-test
+ (async done
+ (try
+ (let [a (await! (js/Promise.resolve 1))
+ b (macros/await! (js/Promise.resolve 1))
+ c (cc/await (js/Promise.resolve 1))
+ d (aw (js/Promise.resolve 1))
+ e (cljs.core/await (js/Promise.resolve 1))
+ f (clojure.core/await (js/Promise.resolve 1))]
+ (is (= 1 a))
+ (is (= 1 b))
+ (is (= 1 c))
+ (is (= 1 d))
+ (is (= 1 e))
+ (is (= 1 f))
+ (done))
+ (catch :default _ (is false)))))
+
+(deftest await-with-ctor
+ (async done
+ (let [f (^:async fn [] (Date. (await (Promise/resolve 0))))]
+ (is (= (Date. 0) (await (f)))))
+ (done)))
+
+(deftest await-with-literals
+ (async done
+ (let [objf (^:async fn [] #js {:foo (await (Promise/resolve "bar"))})]
+ (is (gobj/equals #js {:foo "bar"} (await (objf)))))
+ (let [arrayf (^:async fn [] #js [0 (await (Promise/resolve 1 )) 2])]
+ (is (= [0 1 2] (seq (await (arrayf))))))
+ (let [listf (^:async fn [] (list 0 1 2))]
+ (is (= '(0 1 2) (await (listf)))))
+ (let [vectorf (^:async fn [] [0 (await (Promise/resolve 1 )) 2])]
+ (is (= [0 1 2] (await (vectorf)))))
+ (let [mapf (^:async fn [] {:foo (await (Promise/resolve :bar))})]
+ (is (= {:foo :bar} (await (mapf)))))
+ (let [setf (^:async fn [] #{:foo (await (Promise/resolve :bar)) :baz})]
+ (is (= #{:foo :bar :baz} (await (setf)))))
+ (done)))
+
+(deftest await-with-if-test
+ (async done
+ (let [f (^:async fn [] (if (await (Promise/resolve true)) :success :fail))]
+ (is (= :success (await (f)))))
+ (done)))
+
+(defn ^:async async-destructure
+ [{:keys [foo bar]
+ :or {bar (await (Promise/resolve "hello!"))}}]
+ [foo bar])
+
+(deftest await-in-async-destructure
+ (async done
+ (let [res (await (async-destructure {:foo 1}))]
+ (is (= [1 "hello!"] res)))
+ (done)))
+
+(comment
+ (clojure.test/run-tests)
+ )
diff --git a/src/test/cljs/cljs/chunked_seq.cljs b/src/test/cljs/cljs/chunked_seq.cljs
new file mode 100644
index 0000000000..73abf707f5
--- /dev/null
+++ b/src/test/cljs/cljs/chunked_seq.cljs
@@ -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.
+
+(ns cljs.chunked-seq
+ (:refer-clojure :exclude [iter])
+ (:require [cljs.test :refer-macros [deftest testing is are]]))
+
+(deftest test-cljs-2693
+ (is (chunked-seq? (range 5)))
+ (is (satisfies? IChunk (chunk-first (range 5))))
+ (is (nil? (chunk-next (range 32))))
+ (is (not (chunked-seq? (range 2 -2 0))))
+ (is (chunked-seq? (range)))
+ (is (= 5 (count (chunk-first (range 5)))))
+ (is (= 32 (count (chunk-first (range)))))
+ (is (= 17 (nth (chunk-first (range 100)) 17)))
+ (is (= 35 (nth (chunk-first (range 100)) 35)))
+ (is (= 32 (count (chunk-first (range 100)))))
+ (is (= 0 (first (range 5))))
+ (is (= 1 (second (range 5))))
+ (is (= (range 1 5) (rest (range 5))))
+ (is (= (range 1 5) (next (range 5)))))
\ No newline at end of file
diff --git a/src/test/cljs/cljs/collections_test.cljs b/src/test/cljs/cljs/collections_test.cljs
index 3c85985c08..8607d30c28 100644
--- a/src/test/cljs/cljs/collections_test.cljs
+++ b/src/test/cljs/cljs/collections_test.cljs
@@ -9,20 +9,17 @@
(ns cljs.collections-test
(:refer-clojure :exclude [iter])
(:require [cljs.test :refer-macros [deftest testing is are run-tests]]
- [clojure.test.check :as tc]
[clojure.test.check.clojure-test :refer-macros [defspec]]
[clojure.test.check.generators :as gen]
- [clojure.test.check.properties :as prop :include-macros true]
- [clojure.string :as s]
- [clojure.set :as set]))
+ [clojure.test.check.properties :as prop :include-macros true]))
(deftest test-map-operations
(testing "Test basic map collection operations"
(is (= {:a :b} (get {[1 2 3] {:a :b}, 4 5} [1 2 3])))
(is (not (= {:a :b :c nil} {:a :b :d nil})))
(is (= {:a :b} (dissoc {:a :b :c :d} :c)))
- (is (= (hash-map :foo 5)
- (assoc (cljs.core.ObjMap. nil (array) (js-obj)) :foo 5))))
+ #_(is (= (hash-map :foo 5)
+ (assoc (ObjMap. nil (array) (js-obj)) :foo 5))))
(testing "Testing assoc dissoc"
(is (= {1 2 3 4} (assoc {} 1 2 3 4)))
(is (= {1 2} (assoc {} 1 2)))
@@ -196,8 +193,10 @@
(is (not (counted? (range))))
(is (counted? (range 0 10 1)))
(is (not (counted? (range 0.1 10 1))))
- (is (chunked-seq? (range 0 10 1)))
- (is (chunked-seq? (range 0.1 10 1)))
+ ;; no chunked seqs in :lite-mode
+ (when-not ^boolean LITE_MODE
+ (is (chunked-seq? (range 0 10 1)))
+ (is (chunked-seq? (range 0.1 10 1))))
(is (= (range 0.5 8 1.2) '(0.5 1.7 2.9 4.1 5.3 6.5 7.7)))
(is (= (range 0.5 -4 -2) '(0.5 -1.5 -3.5)))
(testing "IDrop"
@@ -879,12 +878,6 @@
(deftest test-461
;; CLJS-461: automatic map conversions
- (loop [i 0 m (with-meta {} {:foo :bar}) result []]
- (if (<= i (+ cljs.core.ObjMap.HASHMAP_THRESHOLD 2))
- (recur (inc i) (assoc m (str i) i) (conj result (meta m)))
- (let [n (inc (+ cljs.core.ObjMap.HASHMAP_THRESHOLD 2))
- expected (repeat n {:foo :bar})]
- (is (= result expected)))))
(loop [i 0 m (with-meta {-1 :quux} {:foo :bar}) result []]
(if (<= i (+ cljs.core.PersistentArrayMap.HASHMAP_THRESHOLD 2))
(recur (inc i) (assoc m i i) (conj result (meta m)))
@@ -1026,8 +1019,9 @@
(deftest test-cljs-2128
(testing "Subvec iteration"
- (testing "Subvec over PersistentVector uses RangedIterator"
- (is (instance? RangedIterator (-iterator (subvec [0 1 2 3] 1 3)))))
+ (when-not ^boolean LITE_MODE
+ (testing "Subvec over PersistentVector uses RangedIterator"
+ (is (instance? RangedIterator (-iterator (subvec [0 1 2 3] 1 3))))))
(testing "Subvec over other vectors uses naive SeqIter"
(is (instance? SeqIter (-iterator (subvec (->CustomVectorThing [0 1 2 3]) 1 3))))))
(testing "Subvec reduce"
@@ -1133,9 +1127,12 @@
(next (chunk-cons (chunk b) nil))))))
(deftest test-cljs-3124
- (let [t (assoc! (transient []) 0 1)]
- (persistent! t)
- (is (= :fail (try (get t :a :not-found) (catch js/Error e :fail))))))
+ ;; Doesn't work under :lite-mode because there are not
+ ;; separate transient types for now
+ (when-not ^boolean LITE_MODE
+ (let [t (assoc! (transient []) 0 1)]
+ (persistent! t)
+ (is (= :fail (try (get t :a :not-found) (catch js/Error e :fail)))))))
(deftest test-cljs-3317
(testing "persistent vector invoke matches clojure"
@@ -1163,6 +1160,62 @@
(let [things (zipmap (range 15000) (repeat 0))]
(is (zero? (count (filter #(-> % key string?) things))))))
+(deftest test-obj-map
+ (let [a (obj-map)]
+ (is (empty? a))
+ (is (zero? (count a))))
+ (let [b (obj-map :a 1)]
+ (is (not (empty? b)))
+ (is (== 1 (count b))))
+ (let [c (obj-map :a 1 :b 2 :c 3)]
+ (is (== 3 (count c)))
+ (is (= 1 (get c :a)))
+ (is (= 1 (:a c)))
+ (is (every? keyword? (keys c)))
+ (is (= (set [:a :b :c]) (set (keys c)))))
+ (is (= (obj-map :a 1 :b 2 :c 3)
+ (obj-map :a 1 :b 2 :c 3)))
+ (is (= (obj-map :a 1 :b 2)
+ (into (obj-map) [[:a 1] [:b 2]])))
+ (is (= (merge-with +
+ (obj-map :a 1 :b 2)
+ (obj-map :a 1 :b 2))
+ (into (obj-map) [[:a 2] [:b 4]])))
+ (is (= (transient (obj-map :a 1 :b 2))
+ (obj-map :a 1 :b 2))))
+
+(deftest test-hash-map-lite
+ (let [a (hash-map-lite)]
+ (is (empty? a))
+ (is (zero? (count a))))
+ (let [b (hash-map-lite :a 1)]
+ (is (not (empty? b)))
+ (is (== 1 (count b))))
+ (let [c (hash-map-lite :a 1 :b 2 :c 3)]
+ (is (== 3 (count c)))
+ (is (= 1 (get c :a)))
+ (is (= 1 (:a c)))
+ (is (every? keyword? (keys c)))
+ (is (= (set [:a :b :c]) (set (keys c)))))
+ (is (= (hash-map-lite :a 1 :b 2 :c 3)
+ (hash-map-lite :a 1 :b 2 :c 3)))
+ (is (= (hash-map-lite :a 1 :b 2)
+ (into (hash-map-lite) [[:a 1] [:b 2]])))
+ (is (= (merge-with +
+ (hash-map-lite :a 1 :b 2)
+ (hash-map-lite :a 1 :b 2))
+ (into (hash-map-lite) [[:a 2] [:b 4]])))
+ (is (= (transient (hash-map-lite :a 1 :b 2))
+ (hash-map-lite :a 1 :b 2))))
+
+(deftest test-set-lite
+ (is (= #{1 2 3} #{1 2 3}))
+ (is (= 3 (count #{1 2 3})))
+ (let [x #{1 2 3}]
+ (is (every? #(contains? x %) [1 2 3])))
+ (is (= (set-lite [[3 4] [1 2] [5 6]])
+ (into #{} [[3 4] [1 2] [5 6]]))))
+
(comment
(run-tests)
diff --git a/src/test/cljs/cljs/core_test.cljs b/src/test/cljs/cljs/core_test.cljs
index 9c4a625286..01f6f0b527 100644
--- a/src/test/cljs/cljs/core_test.cljs
+++ b/src/test/cljs/cljs/core_test.cljs
@@ -7,6 +7,7 @@
; You must not remove this notice, or any other, from this software.
(ns cljs.core-test
+ (:refer-global :only [Object String])
(:refer-clojure :exclude [iter])
(:require [cljs.test :refer-macros [deftest testing is are]]
[clojure.test.check :as tc]
@@ -196,12 +197,12 @@
(is (= #{:cljs.core-test/rect :cljs.core-test/square} (descendants ::shape)))
(is (true? (isa? 42 42)))
(is (true? (isa? ::square ::shape)))
- (derive cljs.core.ObjMap ::collection)
+ (derive ObjMap ::collection)
(derive cljs.core.PersistentHashSet ::collection)
- (is (true? (isa? cljs.core.ObjMap ::collection)))
+ (is (true? (isa? ObjMap ::collection)))
(is (true? (isa? cljs.core.PersistentHashSet ::collection)))
(is (false? (isa? cljs.core.IndexedSeq ::collection)))
- ;; ?? (isa? String Object)
+ (isa? js/String js/Object)
(is (true? (isa? [::square ::rect] [::shape ::shape])))
;; ?? (ancestors java.util.ArrayList)
;; ?? isa? based dispatch tests
@@ -408,102 +409,6 @@
(halt-when :anomaly #(assoc %2 :partial-results %1))
[1 2 {:anomaly :oh-no!} 3 4]))))
-(deftest test-obj-equiv
- (testing "Object equiv method"
- (is (.equiv :foo :foo))
- (is (.equiv 'foo 'foo))
- (is (.equiv {:foo 1 :bar 2} {:foo 1 :bar 2}))
- (is (.equiv [1 2 3] [1 2 3]))
- (is (.equiv '(1 2 3) '(1 2 3)))
- (is (.equiv (map inc [1 2 3]) (map inc [1 2 3])))
- (is (.equiv #{:cat :dog :bird} #{:cat :dog :bird}))
- ))
-
-(defn seq-iter-match
- [coll]
- (let [i (-iterator coll)]
- (loop [s (seq coll)
- n 0]
- (if (seq s)
- (do
- (when-not (.hasNext i)
- (throw
- (js/Error.
- (str "Iterator exhausted before seq at(" n ")" ))))
- (let [iv (.next i)
- sv (first s)]
- (when-not (= iv sv)
- (throw
- (js/Error.
- (str "Iterator value " iv " and seq value " sv " did not match at ( " n ")")))))
- (recur (rest s) (inc n)))
- (if (.hasNext i)
- (throw
- (js/Error.
- (str "Seq exhausted before iterator at (" n ")")))
- true)))))
-
-(defrecord TestIterRec [a b])
-
-(deftest coll-iter-seq-match
- (testing "Direct iterators match sequences"
- (let [test-map (apply hash-map (range 200))
- test-set (apply hash-set (range 200))
- test-queue (into cljs.core.PersistentQueue.EMPTY (vec (range 100)))
- test-record (into (TestIterRec. 1 2) {:c 3 :d 4})]
- (is (= true (seq-iter-match test-map)))
- (is (= true (seq-iter-match test-set)))
- (is (= true (seq-iter-match test-queue)))
- (is (= true (seq-iter-match test-record))))))
-
-(deftest test-es6-interfaces
- (testing "ES6 collection interfaces"
- (let [iter (es6-iterator [1 2 3])]
- (testing "basic iterations"
- (is (= (.-value (.next iter)) 1))
- (is (= (.-value (.next iter)) 2))
- (is (= (.-value (.next iter)) 3))
- (is (.-done (.next iter)))))
- (is (.has {:foo "bar"} :foo))
- (is (= (.get {:foo "bar"} :foo) "bar"))
- (is (= (.get {:foo "bar"} :bar :default) :default))
- (let [iter (.keys {:foo "bar" :baz "woz"})]
- (testing "map key iteration"
- (is (#{:foo :baz} (.-value (.next iter))))
- (is (#{:foo :baz} (.-value (.next iter))))
- (is (.-done (.next iter)))))
- (let [eiter (.entries {:foo "bar" :baz "woz"})]
- (testing "map entry iteration"
- (let [entries #{(seq #js [:foo "bar"]) (seq #js [:baz "woz"])}]
- (is (entries (seq (.-value (.next eiter)))))
- (is (entries (seq (.-value (.next eiter))))))
- (is (.-done (.next eiter)))))
- (let [iter (.values {:foo "bar" :baz "woz"})]
- (testing "map value iteration"
- (is (#{"bar" "woz"} (.-value (.next iter))))
- (is (#{"bar" "woz"} (.-value (.next iter))))
- (is (.-done (.next iter)))))
- (is (.has #{:cat :bird :dog} :bird))
- (let [iter (.keys #{:cat :bird :dog})]
- (testing "set key iteration"
- (is (#{:cat :bird :dog} (.-value (.next iter))))
- (is (#{:cat :bird :dog} (.-value (.next iter))))
- (is (#{:cat :bird :dog} (.-value (.next iter))))
- (is (.-done (.next iter)))))
- (let [iter (.entries #{:cat :bird :dog})]
- (testing "set entry iteration"
- (is (#{[:cat :cat] [:bird :bird] [:dog :dog]} (seq (.-value (.next iter)))))
- (is (#{[:cat :cat] [:bird :bird] [:dog :dog]} (seq (.-value (.next iter)))))
- (is (#{[:cat :cat] [:bird :bird] [:dog :dog]} (seq (.-value (.next iter)))))
- (is (.-done (.next iter)))))
- (let [iter (.values #{:cat :bird :dog})]
- (testing "set value iteration"
- (is (#{:cat :bird :dog} (.-value (.next iter))))
- (is (#{:cat :bird :dog} (.-value (.next iter))))
- (is (#{:cat :bird :dog} (.-value (.next iter))))
- (is (.-done (.next iter)))))
-))
-
(deftest test-reader-literals
(testing "Testing reader literals"
(is (= #queue [1] (into cljs.core.PersistentQueue.EMPTY [1])))
@@ -833,6 +738,27 @@
(is (= #{1 2} (hash-set 1 2 2)))
(is (= #{1 2} (apply hash-set [1 2 2]))))
+(deftest test-ordered-set
+ (is (= #{1 2} (sorted-set 1 2 2)))
+ (is (= [1 2 3] (seq (sorted-set 2 3 1))))
+ (is (= #{1 2} (apply sorted-set [1 2 2]))))
+
+(deftest test-3454-conj
+ (is (= #{1 2 3} (conj #{1 2} 3)))
+ (is (= #{1 2 3} (conj (sorted-set 1 2) 3)))
+ (let [s #{1 2}
+ ss (sorted-set 1 2)]
+ (is (identical? s (conj s 2)))
+ (is (identical? ss (conj ss 2)))))
+
+(deftest test-3454-disj
+ (is (= #{1 2} (disj #{1 2 3} 3)))
+ (is (= #{1 2} (disj (sorted-set 1 2 3) 3)))
+ (let [s #{1 2}
+ ss (sorted-set 1 2)]
+ (is (identical? s (disj s 3)))
+ (is (identical? ss (disj ss 3)))))
+
(deftest test-585
(is (= (last (map identity (into [] (range 32)))) 31))
(is (= (into #{} (range 32))
@@ -1232,7 +1158,11 @@
(is (= "_DOT__DOT_" (munge "..")))
(is (= "abstract$" (munge "abstract")))
(is (= 'abc (munge 'abc)))
- (is (= "toString" (munge "toString"))))
+ (is (= "toString" (munge "toString")))
+ (is (= "function$" (munge "function"))))
+
+(deftest test-munge-str
+ (is (= "function" (munge-str "function"))))
(defprotocol IFooBar
(a-method [t]))
@@ -1670,22 +1600,6 @@
;; Make sure we didn't delete the alpha? fn
(is (some? alpha-2585?)))
-(deftest test-cljs-2693
- (is (chunked-seq? (range 5)))
- (is (satisfies? IChunk (chunk-first (range 5))))
- (is (nil? (chunk-next (range 32))))
- (is (not (chunked-seq? (range 2 -2 0))))
- (is (chunked-seq? (range)))
- (is (= 5 (count (chunk-first (range 5)))))
- (is (= 32 (count (chunk-first (range)))))
- (is (= 17 (nth (chunk-first (range 100)) 17)))
- (is (= 35 (nth (chunk-first (range 100)) 35)))
- (is (= 32 (count (chunk-first (range 100)))))
- (is (= 0 (first (range 5))))
- (is (= 1 (second (range 5))))
- (is (= (range 1 5) (rest (range 5))))
- (is (= (range 1 5) (next (range 5)))))
-
(defn fn-2741* ([x]) ([x y]))
(def fn-2741 fn-2741*)
@@ -2056,3 +1970,45 @@
[1 2 {:a 1, :b 2, :c 3}]))
(is (= (test-keys :d 4 {:a 1, :b 2, :c 3})
[1 2 {:d 4, :a 1, :b 2, :c 3}]))))
+
+(deftest test-str_
+ (is (= "" (apply cljs.core/str_ nil)))
+ (is (= "" (apply cljs.core/str_ [])))
+ (is (= "1" (apply cljs.core/str_ 1 [])))
+ (is (= "12" (apply cljs.core/str_ 1 [2])))
+ (is (= "1two:threefour#{:five}[:six]#{:seven}{:eight :nine}"
+ (apply cljs.core/str_ 1 ["two" :three 'four #{:five} [:six] #{:seven} {:eight :nine}])))
+ (is (= "1234" (apply cljs.core/str_ 1 2 [3 4]))))
+
+(deftest test-cljs-3452
+ (let [obj #js {:valueOf (fn [] "dude")
+ :toString (fn [] "correct")}
+ str-fn (fn [x y]
+ (str x obj y "\"foobar\"" 1 :foo nil))]
+ (testing "object is stringified using toString"
+ (is (= "correct6\"foobar\"1:foo" (str-fn nil (+ 1 2 3)))))))
+
+(def test-cljs-3472-var nil)
+(deftest test-cljs-3472
+ (set! test-cljs-3472-var "dude")
+ (is (= "dude" (str test-cljs-3472-var))))
+
+(deftest test-cljs-3425
+ (testing "Incorrect min/max handling of ##NaN"
+ (is (NaN? (min ##NaN 1)))
+ (is (NaN? (min 1 ##NaN)))
+ (is (NaN? (max ##NaN 1)))
+ (is (NaN? (max 1 ##NaN)))))
+
+(deftest test-static-props-methods
+ (is (= [] PersistentVector/EMPTY))
+ (let [f String/fromCharCode]
+ (is (= "A" (f 65)))))
+
+(deftest test-new-method
+ (let [f Object/new]
+ (some? (f))))
+
+(deftest test-instance-method-new
+ (is (= ["FOO" "BAR" "BAZ"]
+ (map String/.toUpperCase ["foo" "bar" "baz"]))))
diff --git a/src/test/cljs/cljs/interop_test.cljs b/src/test/cljs/cljs/interop_test.cljs
new file mode 100644
index 0000000000..f736ea15ca
--- /dev/null
+++ b/src/test/cljs/cljs/interop_test.cljs
@@ -0,0 +1,75 @@
+; 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 cljs.interop-test
+ (:refer-clojure :exclude [iter])
+ (:require [cljs.test :refer-macros [deftest testing is are]]))
+
+(deftest test-obj-equiv
+ (testing "Object equiv method"
+ (is (.equiv :foo :foo))
+ (is (.equiv 'foo 'foo))
+ ;; .equiv is not a standard thing, primarily for interop
+ ;; in transit-js, probably not a concern for lite-mode users
+ (when-not LITE_MODE
+ (is (.equiv {:foo 1 :bar 2} {:foo 1 :bar 2})))
+ (is (.equiv [1 2 3] [1 2 3]))
+ (is (.equiv '(1 2 3) '(1 2 3)))
+ (is (.equiv (map inc [1 2 3]) (map inc [1 2 3])))
+ ;; .equiv is not a standard thing, primarily for interop
+ ;; in transit-js, probably not a concern for lite-mode users
+ (when-not LITE_MODE
+ (is (.equiv #{:cat :dog :bird} #{:cat :dog :bird})))))
+
+(deftest test-es6-interfaces
+ (testing "ES6 collection interfaces"
+ (let [iter (es6-iterator [1 2 3])]
+ (testing "basic iterations"
+ (is (= (.-value (.next iter)) 1))
+ (is (= (.-value (.next iter)) 2))
+ (is (= (.-value (.next iter)) 3))
+ (is (.-done (.next iter)))))
+ (is (.has {:foo "bar"} :foo))
+ (is (= (.get {:foo "bar"} :foo) "bar"))
+ (is (= (.get {:foo "bar"} :bar :default) :default))
+ (let [iter (.keys {:foo "bar" :baz "woz"})]
+ (testing "map key iteration"
+ (is (#{:foo :baz} (.-value (.next iter))))
+ (is (#{:foo :baz} (.-value (.next iter))))
+ (is (.-done (.next iter)))))
+ (let [eiter (.entries {:foo "bar" :baz "woz"})]
+ (testing "map entry iteration"
+ (let [entries #{(seq #js [:foo "bar"]) (seq #js [:baz "woz"])}]
+ (is (entries (seq (.-value (.next eiter)))))
+ (is (entries (seq (.-value (.next eiter))))))
+ (is (.-done (.next eiter)))))
+ (let [iter (.values {:foo "bar" :baz "woz"})]
+ (testing "map value iteration"
+ (is (#{"bar" "woz"} (.-value (.next iter))))
+ (is (#{"bar" "woz"} (.-value (.next iter))))
+ (is (.-done (.next iter)))))
+ (is (.has #{:cat :bird :dog} :bird))
+ (let [iter (.keys #{:cat :bird :dog})]
+ (testing "set key iteration"
+ (is (#{:cat :bird :dog} (.-value (.next iter))))
+ (is (#{:cat :bird :dog} (.-value (.next iter))))
+ (is (#{:cat :bird :dog} (.-value (.next iter))))
+ (is (.-done (.next iter)))))
+ (let [iter (.entries #{:cat :bird :dog})]
+ (testing "set entry iteration"
+ (is (#{[:cat :cat] [:bird :bird] [:dog :dog]} (seq (.-value (.next iter)))))
+ (is (#{[:cat :cat] [:bird :bird] [:dog :dog]} (seq (.-value (.next iter)))))
+ (is (#{[:cat :cat] [:bird :bird] [:dog :dog]} (seq (.-value (.next iter)))))
+ (is (.-done (.next iter)))))
+ (let [iter (.values #{:cat :bird :dog})]
+ (testing "set value iteration"
+ (is (#{:cat :bird :dog} (.-value (.next iter))))
+ (is (#{:cat :bird :dog} (.-value (.next iter))))
+ (is (#{:cat :bird :dog} (.-value (.next iter))))
+ (is (.-done (.next iter)))))
+ ))
diff --git a/src/test/cljs/cljs/iterator_test.cljs b/src/test/cljs/cljs/iterator_test.cljs
new file mode 100644
index 0000000000..84e9e6a4d0
--- /dev/null
+++ b/src/test/cljs/cljs/iterator_test.cljs
@@ -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 cljs.iterator-test
+ (:require [cljs.test :refer-macros [deftest testing is are run-tests]]))
+
+(defn seq-iter-match
+ [coll]
+ (let [i (-iterator coll)]
+ (loop [s (seq coll)
+ n 0]
+ (if (seq s)
+ (do
+ (when-not (.hasNext i)
+ (throw
+ (js/Error.
+ (str "Iterator exhausted before seq at(" n ")" ))))
+ (let [iv (.next i)
+ sv (first s)]
+ (when-not (= iv sv)
+ (throw
+ (js/Error.
+ (str "Iterator value " iv " and seq value " sv " did not match at ( " n ")")))))
+ (recur (rest s) (inc n)))
+ (if (.hasNext i)
+ (throw
+ (js/Error.
+ (str "Seq exhausted before iterator at (" n ")")))
+ true)))))
+
+(defrecord TestIterRec [a b])
+
+(deftest coll-iter-seq-match
+ (testing "Direct iterators match sequences"
+ (let [test-map (apply hash-map (range 200))
+ test-set (apply hash-set (range 200))
+ test-queue (into cljs.core.PersistentQueue.EMPTY (vec (range 100)))
+ test-record (into (TestIterRec. 1 2) {:c 3 :d 4})]
+ (is (= true (seq-iter-match test-map)))
+ (is (= true (seq-iter-match test-set)))
+ (is (= true (seq-iter-match test-queue)))
+ (is (= true (seq-iter-match test-record))))))
\ No newline at end of file
diff --git a/src/test/cljs/cljs/lite_collections_test.cljs b/src/test/cljs/cljs/lite_collections_test.cljs
new file mode 100644
index 0000000000..56d44d0ebd
--- /dev/null
+++ b/src/test/cljs/cljs/lite_collections_test.cljs
@@ -0,0 +1,46 @@
+; 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 cljs.lite-collections-test
+ (:require [cljs.test :refer [deftest testing is]]))
+
+;; NOTE: ** this namespace must be tested with :lite-mode true **
+
+(deftest test-obj-map
+ (let [a (. ObjMap -EMPTY)
+ b {}]
+ (is (identical? a b)))
+ (let [a {:foo 1}]
+ (is (== 1 (:foo a)))))
+
+(deftest test-set-lite-with-set
+ (is (= (set-lite []) (set [])))
+ (is (= (set []) (set-lite [])))
+ (is (= (set-lite ["foo" "bar"]) (set-lite ["foo" "bar"])))
+ (is (= (set-lite ["foo" "bar"]) (set-lite #js ["foo" "bar"])))
+ (is (= (set-lite [(MapEntry. 1 2 nil)])
+ (set [(MapEntry. 1 2 nil)]))))
+
+(deftest test-obj-map-clj->js
+ (= 1 (aget (clj->js (obj-map :x 1)) "x"))
+ (= 1 (aget (clj->js {:x 1}) "x")))
+
+(deftest test-unchanged-identical?
+ (let [m (obj-map :foo 1)]
+ (identical? m (assoc m :foo 1)))
+ (let [m (hash-map-lite :foo 1)]
+ (identical? m (assoc m :foo 1)))
+ (let [s (set-lite [:foo])]
+ (identical? s (conj s :foo))))
+
+(comment
+
+ (require '[cljs.lite-collections-test] :reload)
+ (cljs.test/run-tests)
+
+ )
diff --git a/src/test/cljs/cljs/macro_test/macros.clj b/src/test/cljs/cljs/macro_test/macros.clj
index 1354e8c01f..d572503623 100644
--- a/src/test/cljs/cljs/macro_test/macros.clj
+++ b/src/test/cljs/cljs/macro_test/macros.clj
@@ -14,3 +14,7 @@
(defmacro sm-cljs-3027 []
(sorted-map "a" "b"))
+
+(defmacro await! [x]
+ ;; resolves as clojure.core/await, not cljs.core/await
+ `(await ~x))
diff --git a/src/test/cljs/cljs/metadata_test.cljc b/src/test/cljs/cljs/metadata_test.cljc
index b22c79612e..8f5e994113 100644
--- a/src/test/cljs/cljs/metadata_test.cljc
+++ b/src/test/cljs/cljs/metadata_test.cljc
@@ -109,11 +109,15 @@
(seq-interface-tests (seq []))
(seq-interface-tests (rseq [])))
(testing "Medium"
- (seq-interface-tests (seq [0 1 2 3]))
- (seq-interface-tests (rseq [0 1 2 3])))
+ (testing "seq"
+ (seq-interface-tests (seq [0 1 2 3])))
+ (testing "rseq"
+ (seq-interface-tests (rseq [0 1 2 3]))))
(testing "Large"
- (seq-interface-tests (seq (vec (range 100))))
- (seq-interface-tests (rseq (vec (range 100))))))
+ (testing "seq"
+ (seq-interface-tests (seq (vec (range 100)))))
+ (testing "rseq"
+ (seq-interface-tests (rseq (vec (range 100)))))))
(testing "PersistentHashSet"
(testing "Empty"
diff --git a/src/test/cljs/cljs/printing_test.cljs b/src/test/cljs/cljs/printing_test.cljs
index c114893dfc..008172ed1f 100644
--- a/src/test/cljs/cljs/printing_test.cljs
+++ b/src/test/cljs/cljs/printing_test.cljs
@@ -74,6 +74,7 @@
(is (= (pr-str 1) "1"))
(is (= (pr-str -1) "-1"))
(is (= (pr-str -1.5) "-1.5"))
+ (is (= (pr-str -0.0) "-0.0"))
(is (= (pr-str [3 4]) "[3 4]"))
(is (= (pr-str "foo") "\"foo\""))
(is (= (pr-str :hello) ":hello"))
diff --git a/src/test/cljs/cljs/proxy_test.cljs b/src/test/cljs/cljs/proxy_test.cljs
new file mode 100644
index 0000000000..36b53d7cdf
--- /dev/null
+++ b/src/test/cljs/cljs/proxy_test.cljs
@@ -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 cljs.proxy-test
+ (:refer-global :only [Object])
+ (:require [cljs.test :as test :refer-macros [deftest testing is are]]
+ [cljs.proxy :refer [builder]]
+ [goog.object :as gobj]))
+
+(def proxy (builder))
+
+(deftest map-proxy
+ (let [proxied (proxy {:foo 1 :bar 2})]
+ (is (== 1 (gobj/get proxied "foo")))
+ (is (== 2 (gobj/get proxied "bar")))
+ (is (= #{"foo" "bar"} (into #{} (Object/keys proxied))))))
+
+(deftest vector-proxy
+ (let [proxied (proxy [1 2 3 4])]
+ (is (== 4 (alength proxied)))
+ (is (== 1 (aget proxied 0)))
+ (is (== 4 (aget proxied 3)))))
+
+(comment
+
+ (test/run-tests)
+
+)
diff --git a/src/test/cljs/cljs/qualified_method_test.cljs b/src/test/cljs/cljs/qualified_method_test.cljs
new file mode 100644
index 0000000000..df339b6b86
--- /dev/null
+++ b/src/test/cljs/cljs/qualified_method_test.cljs
@@ -0,0 +1,17 @@
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html at the root of this distribution.
+; By using this software in any fashion, you are agreeing to be bound by
+; the terms of this license.
+; You must not remove this notice, or any other, from this software.
+
+(ns cljs.qualified-method-test
+ (:refer-global :only [String])
+ (:require [cljs.test :as test :refer-macros [deftest testing is]]))
+
+(deftest qualified-method-return-position-test
+ (testing "qualified method returned from function to force it in return position"
+ (let [f (fn [] String/.toUpperCase)
+ m (f)]
+ (is (= "FOO" (m "foo"))))))
diff --git a/src/test/cljs/cljs/seqs_test.cljs b/src/test/cljs/cljs/seqs_test.cljs
index 9e43a7340b..00d648fa17 100644
--- a/src/test/cljs/cljs/seqs_test.cljs
+++ b/src/test/cljs/cljs/seqs_test.cljs
@@ -40,7 +40,11 @@
(testing "lazy seq"
(is (seq? e-lazy-seq))
(is (empty? e-lazy-seq))
- (is (= {:b :c} (meta e-lazy-seq)))))
+ ;; MAYBE FIXME: this is a bad test, discovered from :lite-mode work
+ (if-not ^boolean LITE_MODE
+ (is (= {:b :c} (meta e-lazy-seq)))
+ ;; LITE_MODE has the correct behavior
+ (is (nil? (meta e-lazy-seq))))))
(let [e-list (empty '^{:b :c} (1 2 3))]
(testing "list"
(is (seq? e-list))
@@ -203,20 +207,21 @@
(is (= (.lastIndexOf (sequence (map inc) '(0 1 0 3 4)) 1) 2))))
(deftest test-chunked
- (let [r (range 64)
- v (into [] r)]
- (testing "Testing Chunked Seqs"
- (is (= (hash (seq v)) (hash (seq v))))
- (is (= 6 (reduce + (array-chunk (array 1 2 3)))))
- (is (instance? ChunkedSeq (seq v)))
- (is (= r (seq v)))
- (is (= (map inc r) (map inc v)))
- (is (= (filter even? r) (filter even? v)))
- (is (= (filter odd? r) (filter odd? v)))
- (is (= (concat r r r) (concat v v v)))
- (is (satisfies? IReduce (seq v)))
- (is (== 2010 (reduce + (nnext (nnext (seq v))))))
- (is (== 2020 (reduce + 10 (nnext (nnext (seq v)))))))))
+ (when-not LITE_MODE
+ (let [r (range 64)
+ v (into [] r)]
+ (testing "Testing Chunked Seqs"
+ (is (= (hash (seq v)) (hash (seq v))))
+ (is (= 6 (reduce + (array-chunk (array 1 2 3)))))
+ (is (instance? ChunkedSeq (seq v)))
+ (is (= r (seq v)))
+ (is (= (map inc r) (map inc v)))
+ (is (= (filter even? r) (filter even? v)))
+ (is (= (filter odd? r) (filter odd? v)))
+ (is (= (concat r r r) (concat v v v)))
+ (is (satisfies? IReduce (seq v)))
+ (is (== 2010 (reduce + (nnext (nnext (seq v))))))
+ (is (== 2020 (reduce + 10 (nnext (nnext (seq v))))))))))
(deftest test-778
(testing "Testing CLJS-778, -rest, -next RSeq"
diff --git a/src/test/cljs/cljs/walk_test.cljs b/src/test/cljs/cljs/walk_test.cljs
index 9f28ebf082..fb3476f534 100644
--- a/src/test/cljs/cljs/walk_test.cljs
+++ b/src/test/cljs/cljs/walk_test.cljs
@@ -51,28 +51,29 @@
(defmethod get-comparator PersistentTreeSet
[o] (get-comparator (.-tree-map o)))
-(deftest walk
- "Checks that walk returns the correct result and type of collection"
- (let [colls ['(1 2 3)
- [1 2 3]
- #{1 2 3}
- (sorted-set-by > 1 2 3)
- {:a 1, :b 2, :c 3}
- (sorted-map-by > 1 10, 2 20, 3 30)
- (->Foo 1 2 3)
- (map->Foo {:a 1 :b 2 :c 3 :extra 4})]]
- (doseq [c colls]
- (let [walked (w/walk identity identity c)]
- (is (= c walked))
- ;;(is (= (type c) (type walked)))
- (if (map? c)
- (is (= (w/walk #(update-in % [1] inc) #(reduce + (vals %)) c)
- (reduce + (map (comp inc val) c))))
- (is (= (w/walk inc #(reduce + %) c)
- (reduce + (map inc c)))))
- (when (or (instance? PersistentTreeMap c)
- (instance? PersistentTreeSet c))
- (is (= (get-comparator c) (get-comparator walked))))))))
+(deftest test-walk
+ (testing "Test that walk returns the correct result\n"
+ (let [colls ['(1 2 3)
+ [1 2 3]
+ #{1 2 3}
+ (sorted-set-by > 1 2 3)
+ {:a 1, :b 2, :c 3}
+ (sorted-map-by > 1 10, 2 20, 3 30)
+ (->Foo 1 2 3)
+ (map->Foo {:a 1 :b 2 :c 3 :extra 4})]]
+ (doseq [c colls]
+ (testing (str "Walking ... " c)
+ (let [walked (w/walk identity identity c)]
+ (is (= c walked))
+ ;;(is (= (type c) (type walked)))
+ (if (map? c)
+ (is (= (w/walk #(update-in % [1] inc) #(reduce + (vals %)) c)
+ (reduce + (map (comp inc val) c))))
+ (is (= (w/walk inc #(reduce + %) c)
+ (reduce + (map inc c)))))
+ (when (or (instance? PersistentTreeMap c)
+ (instance? PersistentTreeSet c))
+ (is (= (get-comparator c) (get-comparator walked))))))))))
(deftest walk-mapentry
"Checks that walk preserves the MapEntry type. See CLJS-2909."
diff --git a/src/test/cljs/lite_test_runner.cljs b/src/test/cljs/lite_test_runner.cljs
new file mode 100644
index 0000000000..c9f58d677f
--- /dev/null
+++ b/src/test/cljs/lite_test_runner.cljs
@@ -0,0 +1,130 @@
+;; 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 lite-test-runner
+ (:require [cljs.qualified-method-test]
+ [cljs.proxy-test]
+ [cljs.test :refer-macros [run-tests]]
+ [cljs.apply-test]
+ [cljs.primitives-test]
+ [cljs.destructuring-test]
+ [cljs.new-new-test]
+ [cljs.printing-test]
+ [cljs.seqs-test]
+ [cljs.collections-test]
+ [cljs.hashing-test]
+ [cljs.core-test]
+ ;; [cljs.chunked-seq] ;; doesn't exist in :lite-mode
+ [cljs.interop-test]
+ [cljs.iterator-test]
+ [cljs.reader-test]
+ [cljs.binding-test]
+ [cljs.parse-test]
+ [cljs.ns-test]
+ [clojure.set-test]
+ [clojure.string-test]
+ [clojure.data-test]
+ [clojure.datafy-test]
+ [clojure.edn-test]
+ [clojure.walk-test]
+ [clojure.math-test]
+ [cljs.macro-test]
+ [cljs.letfn-test]
+ [foo.ns-shadow-test]
+ [cljs.top-level]
+ [cljs.reducers-test]
+ [cljs.keyword-test]
+ [cljs.import-test]
+ [cljs.ns-test.foo]
+ [cljs.syntax-quote-test]
+ [cljs.pprint]
+ [cljs.pprint-test]
+ [cljs.spec-test]
+ [cljs.specials-test]
+ [cljs.spec.test-test]
+ [cljs.clojure-alias-test]
+ ;; [cljs.hash-map-test]
+ ;; [cljs.map-entry-test]
+ [cljs.metadata-test]
+ [cljs.npm-deps-test]
+ [cljs.other-functions-test]
+ [cljs.predicates-test]
+ [cljs.tagged-literals-test]
+ [cljs.test-test]
+ [static.core-test]
+ [cljs.recur-test]
+ [cljs.array-access-test]
+ [cljs.inference-test]
+ [cljs.walk-test]
+ [cljs.repl-test]
+ [cljs.lite-collections-test]
+ [cljs.extend-to-native-test]
+ [cljs.var-test]))
+
+(set! *print-newline* false)
+
+;; When testing Windows we default to Node.js
+(if (exists? js/print)
+ (set-print-fn! js/print)
+ (enable-console-print!))
+
+(run-tests
+ 'cljs.qualified-method-test
+ 'cljs.proxy-test
+ 'cljs.apply-test
+ 'cljs.primitives-test
+ 'cljs.destructuring-test
+ 'cljs.printing-test
+ 'cljs.new-new-test
+ 'cljs.seqs-test
+ 'cljs.collections-test
+ 'cljs.hashing-test
+ 'cljs.core-test
+ 'cljs.interop-test ;; ES6 stuff
+ 'cljs.iterator-test
+ 'cljs.reader-test
+ 'cljs.binding-test
+ 'cljs.parse-test
+ 'cljs.ns-test
+ 'clojure.set-test
+ 'clojure.string-test
+ 'clojure.data-test
+ 'clojure.datafy-test
+ 'clojure.edn-test
+ 'clojure.walk-test
+ 'clojure.math-test
+ 'cljs.macro-test
+ 'cljs.letfn-test
+ 'foo.ns-shadow-test
+ 'cljs.top-level
+ 'cljs.reducers-test
+ 'cljs.keyword-test
+ 'cljs.import-test
+ 'cljs.ns-test.foo
+ 'cljs.syntax-quote-test
+ 'cljs.pprint
+ 'cljs.pprint-test
+ 'cljs.spec-test
+ 'cljs.specials-test
+ 'cljs.spec.test-test
+ 'cljs.clojure-alias-test
+ 'cljs.metadata-test
+ 'cljs.npm-deps-test
+ 'cljs.other-functions-test
+ 'cljs.predicates-test
+ 'cljs.tagged-literals-test
+ 'cljs.test-test
+ 'static.core-test
+ 'cljs.recur-test
+ 'cljs.array-access-test
+ 'cljs.inference-test
+ 'cljs.walk-test
+ 'cljs.repl-test
+ 'cljs.lite-collections-test
+ 'cljs.extend-to-native-test
+ 'cljs.var-test)
diff --git a/src/test/cljs/test_runner.cljs b/src/test/cljs/test_runner.cljs
index a0e4113094..0488aa4778 100644
--- a/src/test/cljs/test_runner.cljs
+++ b/src/test/cljs/test_runner.cljs
@@ -7,7 +7,10 @@
;; You must not remove this notice, or any other, from this software.
(ns test-runner
- (:require [cljs.test :refer-macros [run-tests]]
+ (:require [cljs.async-await-test]
+ [cljs.qualified-method-test]
+ [cljs.proxy-test]
+ [cljs.test :refer-macros [run-tests]]
[cljs.apply-test]
[cljs.primitives-test]
[cljs.destructuring-test]
@@ -17,6 +20,9 @@
[cljs.collections-test]
[cljs.hashing-test]
[cljs.core-test :as core-test]
+ [cljs.chunked-seq]
+ [cljs.interop-test]
+ [cljs.iterator-test]
[cljs.reader-test]
[cljs.binding-test]
[cljs.parse-test]
@@ -68,6 +74,9 @@
(enable-console-print!))
(run-tests
+ 'cljs.async-await-test
+ 'cljs.qualified-method-test
+ 'cljs.proxy-test
'cljs.apply-test
'cljs.primitives-test
'cljs.destructuring-test
@@ -77,6 +86,9 @@
'cljs.collections-test
'cljs.hashing-test
'cljs.core-test
+ 'cljs.chunked-seq
+ 'cljs.interop-test
+ 'cljs.iterator-test
'cljs.reader-test
'cljs.parse-test
'clojure.set-test
diff --git a/src/test/cljs_build/adv_src_map/core.cljs b/src/test/cljs_build/adv_src_map/core.cljs
new file mode 100644
index 0000000000..22cc32cab7
--- /dev/null
+++ b/src/test/cljs_build/adv_src_map/core.cljs
@@ -0,0 +1,3 @@
+(ns adv-src-map.core)
+
+(.log js/console "Hello!" (first [1 2 3]))
\ No newline at end of file
diff --git a/src/test/cljs_build/cljs_3452_str_optimizations/core.cljs b/src/test/cljs_build/cljs_3452_str_optimizations/core.cljs
new file mode 100644
index 0000000000..75456c406c
--- /dev/null
+++ b/src/test/cljs_build/cljs_3452_str_optimizations/core.cljs
@@ -0,0 +1,9 @@
+(ns cljs-3452-str-optimizations.core)
+
+(defn my-str-fn [x y]
+ (str x y nil ::foobar "my
+
+multiline
+
+string with `backticks`"
+ true false 3.14))
diff --git a/src/test/cljs_build/trivial/core2.cljs b/src/test/cljs_build/trivial/core2.cljs
new file mode 100644
index 0000000000..a79e64e807
--- /dev/null
+++ b/src/test/cljs_build/trivial/core2.cljs
@@ -0,0 +1,3 @@
+(ns trivial.core2)
+
+(.log js/console (-lookup 1 2))
diff --git a/src/test/cljs_build/trivial/core3.cljs b/src/test/cljs_build/trivial/core3.cljs
new file mode 100644
index 0000000000..a66db571c3
--- /dev/null
+++ b/src/test/cljs_build/trivial/core3.cljs
@@ -0,0 +1,3 @@
+(ns trivial.core3)
+
+(.log js/console :foo)
diff --git a/src/test/cljs_build/trivial/core4.cljs b/src/test/cljs_build/trivial/core4.cljs
new file mode 100644
index 0000000000..f8f4c6d25b
--- /dev/null
+++ b/src/test/cljs_build/trivial/core4.cljs
@@ -0,0 +1,3 @@
+(ns trivial.core4)
+
+(.log js/console [])
diff --git a/src/test/cljs_build/trivial/core5.cljs b/src/test/cljs_build/trivial/core5.cljs
new file mode 100644
index 0000000000..1e7f877568
--- /dev/null
+++ b/src/test/cljs_build/trivial/core5.cljs
@@ -0,0 +1,3 @@
+(ns trivial.core5)
+
+(.log js/console {})
\ No newline at end of file
diff --git a/src/test/cljs_build/trivial/core6.cljs b/src/test/cljs_build/trivial/core6.cljs
new file mode 100644
index 0000000000..3eed31bc65
--- /dev/null
+++ b/src/test/cljs_build/trivial/core6.cljs
@@ -0,0 +1,3 @@
+(ns trivial.core6)
+
+(.log js/console (->> (map inc (range 10)) (filter even?) (partition 2) (drop 1) (mapcat identity) into-array))
diff --git a/src/test/clojure/cljs/analyzer_pass_tests.clj b/src/test/clojure/cljs/analyzer_pass_tests.clj
index c87ec7bceb..643352b7ac 100644
--- a/src/test/clojure/cljs/analyzer_pass_tests.clj
+++ b/src/test/clojure/cljs/analyzer_pass_tests.clj
@@ -178,8 +178,27 @@
(map (fn [x] x) s))))]))))]
(is (empty? (re-seq #"or_" code))))))
+(deftest test-lite-mode-pass
+ (let [aenv (assoc (ana/empty-env) :context :expr)
+ env (env/default-compiler-env {:lite-mode true})]
+ (let [ast (env/with-compiler-env env
+ (comp/with-core-cljs {}
+ (fn []
+ (analyze aenv 'cljs.core/vec))))]
+ (is (= 'cljs.core/vec-lite
+ (-> ast :name)
+ (-> ast :info :name))))
+ (let [ast (env/with-compiler-env env
+ (comp/with-core-cljs {}
+ (fn []
+ (analyze aenv 'cljs.core/vector))))]
+ (is (= 'cljs.core/vector-lite
+ (-> ast :name)
+ (-> ast :info :name))))))
+
(comment
(test/run-tests)
(require '[clojure.pprint :refer [pprint]])
+
)
diff --git a/src/test/clojure/cljs/analyzer_tests.clj b/src/test/clojure/cljs/analyzer_tests.clj
index f1b639938f..07aff9247f 100644
--- a/src/test/clojure/cljs/analyzer_tests.clj
+++ b/src/test/clojure/cljs/analyzer_tests.clj
@@ -171,7 +171,7 @@
(analyze ns-env '(ns foo.bar (:unless [])))
(catch Exception e
(.getMessage (.getCause e))))
- "Only :refer-clojure, :require, :require-macros, :use, :use-macros, and :import libspecs supported. Got (:unless []) instead."))
+ "Only :refer-clojure, :require, :require-macros, :use, :use-macros, :require-global and :import libspecs supported. Got (:unless []) instead."))
(is (.startsWith
(try
(analyze ns-env '(ns foo.bar (:require baz.woz) (:require noz.goz)))
@@ -387,6 +387,32 @@
:renames {map clj-map}}))
(is (set? (:excludes parsed)))))
+(deftest test-parse-global-refer
+ (let [parsed (ana/parse-global-refer-spec {}
+ '((:refer-global :only [Date Symbol] :rename {Symbol JSSymbol})))]
+ (is (= parsed
+ '{:use {Date js}
+ :rename {JSSymbol js/Symbol}}))))
+
+(deftest test-parse-require-global
+ (let [cenv (atom {})
+ deps (atom [])
+ parsed (ana/parse-global-require-spec {} cenv deps (atom {:fns {}})
+ '[React :refer [createElement] :as react])]
+ (println (pr-str @cenv) (pr-str @deps))
+ (is (= parsed
+ '{:require {react React
+ React React}
+ :use {createElement React}})))
+ (let [cenv (atom {})
+ deps (atom [])
+ parsed (ana/parse-global-require-spec {} cenv deps (atom {:fns {}})
+ '[React :refer [createElement] :rename {createElement create} :as react])]
+ (is (= parsed
+ '{:require {react React
+ React React}
+ :rename {create React/createElement}}))))
+
(deftest test-cljs-1785-js-shadowed-by-local
(let [ws (atom [])]
(ana/with-warning-handlers [(collecting-warning-handler ws)]
@@ -547,6 +573,14 @@
(analyze test-env
'(map #(require '[clojure.set :as set]) [1 2]))))))
+(deftest test-analyze-refer-global
+ (testing "refer-global macro expr return expected AST"
+ (binding [ana/*cljs-ns* ana/*cljs-ns*
+ ana/*cljs-warnings* nil]
+ (let [test-env (ana/empty-env)]
+ (is (= (-> (analyze test-env '(refer-global :only '[Date])) :uses vals set)
+ '#{js}))))))
+
(deftest test-gen-user-ns
;; note: can't use `with-redefs` because direct-linking is enabled
(let [s "src/cljs/foo.cljs"
@@ -1533,3 +1567,27 @@
(ana/gen-constant-id '+)))
(is (not= (ana/gen-constant-id 'foo.bar)
(ana/gen-constant-id 'foo$bar))))
+
+;; -----------------------------------------------------------------------------
+;; :refer-global / :require-global ns parsing tests
+
+(deftest test-refer-global
+ (binding [ana/*cljs-ns* ana/*cljs-ns*]
+ (let [parsed-ns (env/with-compiler-env test-cenv
+ (analyze test-env
+ '(ns foo.core
+ (:refer-global :only [Date] :rename {Date MyDate}))))]
+ (= (:renames parsed-ns)
+ '{MyDate js/Date}))))
+
+(deftest test-require-global
+ (binding [ana/*cljs-ns* ana/*cljs-ns*]
+ (let [parsed-ns (env/with-compiler-env test-cenv
+ (analyze test-env
+ '(ns foo.core
+ (:require-global [React :as react :refer [createElement]]))))]
+ (is (= (:requires parsed-ns)
+ '{React React
+ react React}))
+ (is (= (:uses parsed-ns)
+ '{createElement React})))))
diff --git a/src/test/clojure/cljs/build_api_tests.clj b/src/test/clojure/cljs/build_api_tests.clj
index e788c1ace0..caa8da41fa 100644
--- a/src/test/clojure/cljs/build_api_tests.clj
+++ b/src/test/clojure/cljs/build_api_tests.clj
@@ -718,7 +718,107 @@
cenv (env/default-compiler-env)]
(test/delete-out-files out)
(build/build (build/inputs (io/file inputs "trivial/core.cljs")) opts cenv)
- (is (< (.length out-file) 10000))))
+ (is (< (.length out-file) 10240))))
+
+(deftest trivial-output-size-protocol
+ (let [out (.getPath (io/file (test/tmp-dir) "trivial-output-protocol-test-out"))
+ out-file (io/file out "main.js")
+ {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build"))
+ :opts {:main 'trivial.core2
+ :output-dir out
+ :output-to (.getPath out-file)
+ :optimizations :advanced}}
+ cenv (env/default-compiler-env)]
+ (test/delete-out-files out)
+ (build/build (build/inputs (io/file inputs "trivial/core2.cljs")) opts cenv)
+ (is (< (.length out-file) 10240))))
+
+(deftest trivial-output-size-keyword
+ (let [out (.getPath (io/file (test/tmp-dir) "trivial-output-keyword-test-out"))
+ out-file (io/file out "main.js")
+ {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build"))
+ :opts {:main 'trivial.core3
+ :output-dir out
+ :output-to (.getPath out-file)
+ :optimizations :advanced}}
+ cenv (env/default-compiler-env)]
+ (test/delete-out-files out)
+ (build/build (build/inputs (io/file inputs "trivial/core3.cljs")) opts cenv)
+ (is (< (.length out-file) 10240))))
+
+(deftest trivial-output-size-vector
+ (let [out (.getPath (io/file (test/tmp-dir) "trivial-output-vector-test-out"))
+ out-file (io/file out "main.js")
+ {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build"))
+ :opts {:main 'trivial.core4
+ :output-dir out
+ :output-to (.getPath out-file)
+ :optimizations :advanced}}
+ cenv (env/default-compiler-env)]
+ (test/delete-out-files out)
+ (build/build (build/inputs (io/file inputs "trivial/core4.cljs")) opts cenv)
+ (is (< (.length out-file) 92160))))
+
+(deftest lite-mode-vector-code-size-ratchet
+ (testing ":lite-mode + :elide-to-string, should cut output size for [] in 1/2"
+ (let [out (.getPath (io/file (test/tmp-dir) "trivial-output-vector-test-out"))
+ out-file (io/file out "main.js")
+ {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build"))
+ :opts {:main 'trivial.core4
+ :output-dir out
+ :output-to (.getPath out-file)
+ :lite-mode true
+ :elide-to-string true
+ :optimizations :advanced}}
+ cenv (env/default-compiler-env)]
+ (test/delete-out-files out)
+ (build/build (build/inputs (io/file inputs "trivial/core4.cljs")) opts cenv)
+ (is (< (.length out-file) 16384)))))
+
+(deftest trivial-output-size-map
+ (let [out (.getPath (io/file (test/tmp-dir) "trivial-output-map-test-out"))
+ out-file (io/file out "main.js")
+ {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build"))
+ :opts {:main 'trivial.core5
+ :output-dir out
+ :output-to (.getPath out-file)
+ :optimizations :advanced}}
+ cenv (env/default-compiler-env)]
+ (test/delete-out-files out)
+ (build/build (build/inputs (io/file inputs "trivial/core5.cljs")) opts cenv)
+ (is (< (.length out-file) 92160))))
+
+(deftest lite-mode-map-code-size-ratchet
+ (testing ":lite-mode + :elide-to-string, should cut output size for {} in 1/3"
+ (let [out (.getPath (io/file (test/tmp-dir) "trivial-output-map-test-out"))
+ out-file (io/file out "main.js")
+ {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build"))
+ :opts {:main 'trivial.core5
+ :output-dir out
+ :output-to (.getPath out-file)
+ :lite-mode true
+ :elide-to-string true
+ :optimizations :advanced}}
+ cenv (env/default-compiler-env)]
+ (test/delete-out-files out)
+ (build/build (build/inputs (io/file inputs "trivial/core5.cljs")) opts cenv)
+ (is (< (.length out-file) 32768)))))
+
+(deftest lite-mode-api-code-size-ratchet
+ (testing ":lite-mode + :elide-to-string, typical cljs.core api usage ~32K"
+ (let [out (.getPath (io/file (test/tmp-dir) "trivial-output-map-test-out"))
+ out-file (io/file out "main.js")
+ {:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build"))
+ :opts {:main 'trivial.core6
+ :output-dir out
+ :output-to (.getPath out-file)
+ :lite-mode true
+ :elide-to-string true
+ :optimizations :advanced}}
+ cenv (env/default-compiler-env)]
+ (test/delete-out-files out)
+ (build/build (build/inputs (io/file inputs "trivial/core6.cljs")) opts cenv)
+ (is (< (.length out-file) 34000)))))
(deftest cljs-3255-nil-inputs-build
(let [out (.getPath (io/file (test/tmp-dir) "3255-test-out"))
@@ -840,3 +940,48 @@
(.delete (io/file "package.json"))
(test/delete-node-modules)
(test/delete-out-files out))))
+
+#_(deftest test-cljs-3452-str-optimizations
+ (testing "Test that uses compile time optimizations from str macro"
+ (let [out (.getPath (io/file (test/tmp-dir) "cljs-3452-str-optimizations-out"))]
+ (test/delete-out-files out)
+ (let [{:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build"))
+ :opts {:main 'cljs-3452-str-optimizations.core
+ :output-dir out
+ :optimizations :none
+ :closure-warnings {:check-types :off}}}
+ cenv (env/default-compiler-env)]
+ (build/build (build/inputs (io/file inputs "cljs_3452_str_optimizations/core.cljs")) opts cenv))
+ (let [source (slurp (io/file out "cljs_3452_str_optimizations/core.js"))]
+ (testing "only seven string concats, compile time nil is ignored"
+ (is (= 7 (count (re-seq #"[\+]" source)))))
+ (testing "only two 1-arity str calls, compile time constants are optimized"
+ (is (= 2 (count (re-seq #"\$1\(.*?\)" source))))))
+ (test/delete-out-files out))))
+
+#_(deftest test-advanced-source-maps
+ (testing "Test that the `sources` of the final merged source map matches the
+ one in the original Closure Compiler generated advanced source map"
+ (let [out (.getPath (io/file (test/tmp-dir) "adv-src-map"))]
+ (test/delete-out-files out)
+ (test/delete-node-modules)
+ (let [{:keys [inputs opts]} {:inputs (str (io/file "src" "test" "cljs_build"))
+ :opts {:main 'cljs-3346-as-alias.core
+ :output-to (.getPath (io/file out "main.js"))
+ :source-map (.getPath (io/file out "main.js.map"))
+ :output-dir out
+ :optimizations :advanced
+ :closure-source-map true}}
+ cenv (env/default-compiler-env)]
+ (build/build (build/inputs (io/file inputs "adv_src_map/core.cljs")) opts cenv))
+ (let [cljs-src-map (->> (io/file out "main.js.map") slurp json/read-str)
+ closure-src-map (->> (io/file out "main.js.map.closure") slurp json/read-str)]
+ (println (get closure-src-map "sources"))
+ (println (get cljs-src-map "sources")))
+ (test/delete-out-files out))))
+
+#_(comment
+
+ (clojure.test/test-vars [#'test-advanced-source-maps])
+
+ )
\ No newline at end of file
diff --git a/src/test/clojure/cljs/compiler_tests.clj b/src/test/clojure/cljs/compiler_tests.clj
index bb6a9bfc3b..f6f7b560b4 100644
--- a/src/test/clojure/cljs/compiler_tests.clj
+++ b/src/test/clojure/cljs/compiler_tests.clj
@@ -15,7 +15,8 @@
[cljs.util :as util]
[cljs.tagged-literals :as tags]
[clojure.java.io :as io]
- [clojure.string :as str])
+ [clojure.string :as str]
+ [clojure.test :as test])
(:import [java.io File]))
(defn analyze
@@ -374,6 +375,32 @@
window))]))]
(is (re-find #"window__\$1" code)))))
+(deftest test-externs-infer-is-nan
+ (testing "Not calls to truth_ if (.isNaN js/Number ...) is used as a test"
+ (let [code (env/with-compiler-env (env/default-compiler-env)
+ (compile-form-seq
+ '[(if (.isNaN js/Number 1) true false)]))]
+ (is (nil? (re-find #"truth_" code))))))
+
+(deftest test-goog-lib-infer-boolean
+ (testing "Can infer goog.string/contains returns boolean"
+ (let [code (env/with-compiler-env (env/default-compiler-env)
+ (compile-form-seq
+ '[(ns test.foo
+ (:require [goog.string :as gstring]))
+ (if (gstring/contains "foobar" "foo") true false)]))]
+ (is (nil? (re-find #"truth_" code))))))
+
+(deftest test-goog-module-infer-cljs-3438
+ (testing "goog.object/containKey requires correct handling of vars from
+ goog.module namespace"
+ (let [code (env/with-compiler-env (env/default-compiler-env)
+ (compile-form-seq
+ '[(ns test.foo
+ (:require [goog.object :as gobject]))
+ (if (gobject/containsKey nil nil) true false)]))]
+ (is (nil? (re-find #"truth_" code))))))
+
;; CLJS-1225
(comment
diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj
index 8ca7ff9aaa..967164d1f2 100644
--- a/src/test/clojure/cljs/externs_infer_tests.clj
+++ b/src/test/clojure/cljs/externs_infer_tests.clj
@@ -23,6 +23,35 @@
"goog.isArrayLike;" "Java.type;" "Object.out;" "Object.out.println;"
"Object.error;" "Object.error.println;"])
+(deftest test-normalize-js-tag
+ (is (= 'js (ana/normalize-js-tag 'js)))
+ (is (= '[Foo] (-> 'js/Foo ana/normalize-js-tag meta :prefix)))
+ (is (true? (-> 'js/Foo ana/normalize-js-tag meta :prefix last meta :ctor)))
+ (is (= '[Foo Bar] (-> 'js/Foo.Bar ana/normalize-js-tag meta :prefix)))
+ (is (true? (-> 'js/Foo.Bar ana/normalize-js-tag meta :prefix last meta :ctor))))
+
+(deftest test-normalize-unresolved-prefix
+ (let [pre (-> (ana/normalize-js-tag 'js/Foo) meta :prefix (conj 'bar))]
+ (is (= '[Foo prototype bar] (ana/normalize-unresolved-prefix pre))))
+ (let [pre '[Foo bar]]
+ (is (= '[Foo bar] (ana/normalize-unresolved-prefix pre)))))
+
+(comment
+
+ (test/test-vars [#'test-normalize-js-tag])
+ (test/test-vars [#'test-normalize-unresolved-prefix])
+
+ )
+
+(deftest test-resolve-extern
+ (let [externs
+ (externs/externs-map
+ (closure/load-externs
+ {:externs ["src/test/externs/test.js"]
+ :use-only-custom-externs true}))]
+ (is (some? (ana/resolve-extern '[baz] externs)))
+ (is (nil? (ana/resolve-extern '[Foo gozMethod] externs)))))
+
(deftest test-has-extern?-basic
(let [externs (externs/externs-map
(closure/load-externs
@@ -35,6 +64,35 @@
(is (true? (ana/has-extern? '[baz] externs)))
(is (false? (ana/has-extern? '[Baz] externs)))))
+(deftest test-resolve-extern
+ (let [externs (externs/externs-map)]
+ (is (= '[Number]
+ (-> (ana/resolve-extern '[Number] externs) :resolved)))
+ (is (= '[Number prototype valueOf]
+ (-> (ana/resolve-extern '[Number valueOf] externs) :resolved)))
+ (is (= '[Console]
+ (-> (ana/resolve-extern '[console] externs) :resolved)))
+ (is (= '[Console prototype log]
+ (-> (ana/resolve-extern '[console log] externs) :resolved)))
+ (is (= '[undefined]
+ (-> (ana/resolve-extern '[undefined] externs) :resolved)))
+ (is (= '[webCrypto Crypto prototype subtle]
+ (-> (ana/resolve-extern '[crypto subtle] externs) :resolved)))))
+
+(comment
+ (clojure.test/test-vars [#'test-resolve-extern])
+
+ (def externs (externs/externs-map))
+ ;; succeeds
+ (ana/resolve-extern '[console] externs)
+ (ana/resolve-extern '[console log] externs)
+ (ana/resolve-extern '[undefined] externs)
+ (ana/resolve-extern '[Number] externs)
+ (ana/resolve-extern '[Number isNaN] externs)
+ (ana/resolve-extern '[document] externs)
+
+ )
+
(deftest test-has-extern?-defaults
(let [externs (externs/externs-map)]
(is (true? (ana/has-extern? '[console] externs)))
@@ -47,9 +105,16 @@
{:externs ["src/test/externs/test.js"]}))]
(is (= 'js/Console (ana/js-tag '[console] :tag externs)))
(is (= 'js/Function (ana/js-tag '[console log] :tag externs)))
- (is (= 'js/Boolean (ana/js-tag '[Number isNaN] :ret-tag externs)))
+ (is (= 'js/undefined (ana/js-tag '[console log] :ret-tag externs)))
+ (is (= 'boolean (ana/js-tag '[Number isNaN] :ret-tag externs)))
(is (= 'js/Foo (ana/js-tag '[baz] :ret-tag externs)))))
+(comment
+
+ (clojure.test/test-vars [#'test-js-tag])
+
+ )
+
(defn infer-test-helper
[{:keys [forms externs warnings warn js-dependency-index node-module-index with-core? opts]}]
(let [test-cenv (atom
@@ -82,6 +147,54 @@
(map (comp :externs second)
(get @test-cenv ::ana/namespaces))))))))))))
+(deftest test-externs-type-infer
+ (is (= 'boolean
+ (-> (binding [ana/*cljs-ns* ana/*cljs-ns*]
+ (env/with-compiler-env (env/default-compiler-env)
+ (analyze (ana/empty-env) '(.isNaN js/Number 1))))
+ :tag)))
+ (is (= 'boolean
+ (-> (binding [ana/*cljs-ns* ana/*cljs-ns*]
+ (env/with-compiler-env (env/default-compiler-env)
+ (analyze (ana/empty-env) '(js/Number.isNaN 1))))
+ :tag)))
+ (is (= 'boolean
+ (-> (binding [ana/*cljs-ns* ana/*cljs-ns*]
+ (env/with-compiler-env (env/default-compiler-env)
+ (analyze (ana/empty-env) '(let [x js/Number]
+ (.isNaN x 1)))))
+ :tag)))
+ (is (= 'boolean
+ (-> (binding [ana/*cljs-ns* ana/*cljs-ns*]
+ (env/with-compiler-env (env/default-compiler-env)
+ (analyze (ana/empty-env) '(js/isNaN 1))))
+ :tag)))
+ (is (= 'js/Promise
+ (-> (binding [ana/*cljs-ns* ana/*cljs-ns*]
+ (env/with-compiler-env (env/default-compiler-env)
+ (analyze (ana/empty-env) '(.generateKey js/crypto.subtle))))
+ :tag)))
+ (is (= 'string
+ (-> (binding [ana/*cljs-ns* ana/*cljs-ns*]
+ (env/with-compiler-env (env/default-compiler-env)
+ (analyze (ana/empty-env) '(.toUpperCase "foo"))))
+ :tag)))
+ (is (= 'boolean
+ (-> (binding [ana/*cljs-ns* ana/*cljs-ns*]
+ (env/with-compiler-env (env/default-compiler-env)
+ (analyze (ana/empty-env) '(.isArray js/Array (array)))))
+ :tag)))
+ (is (= 'boolean
+ (-> (binding [ana/*cljs-ns* ana/*cljs-ns*]
+ (env/with-compiler-env (env/default-compiler-env)
+ (analyze (ana/empty-env) '(.isSafeInteger js/Number 1))))
+ :tag)))
+ (is (= 'boolean
+ (-> (binding [ana/*cljs-ns* ana/*cljs-ns*]
+ (env/with-compiler-env (env/default-compiler-env)
+ (analyze (ana/empty-env) '(js/isFinite 1))))
+ :tag))))
+
(deftest test-externs-infer
(is (= 'js/Foo
(-> (binding [ana/*cljs-ns* ana/*cljs-ns*]
@@ -158,9 +271,9 @@
:warnings ws})]
(is (= (unsplit-lines ["Foo.Boo.prototype.wozz;"]) res))
(is (= 1 (count @ws)))
- (is (string/starts-with?
- (first @ws)
- "Cannot resolve property wozz for inferred type js/Foo.Boo"))))
+ (is (some-> @ws first
+ (string/starts-with?
+ "Cannot resolve property wozz for inferred type js/Foo.Boo")))))
(deftest test-type-hint-infer-unknown-property-in-chain
(let [ws (atom [])
@@ -172,9 +285,9 @@
:warnings ws})]
(is (= (unsplit-lines ["Foo.Boo.prototype.wozz;"]) res))
(is (= 1 (count @ws)))
- (is (string/starts-with?
- (first @ws)
- "Cannot resolve property wozz for inferred type js/Foo.Boo"))))
+ (is (some-> @ws first
+ (string/starts-with?
+ "Cannot resolve property wozz for inferred type js/Foo.Boo")))))
(deftest test-type-hint-infer-unknown-method
(let [ws (atom [])
@@ -185,9 +298,24 @@
:warnings ws})]
(is (= (unsplit-lines ["Foo.prototype.gozMethod;"]) res))
(is (= 1 (count @ws)))
- (is (string/starts-with?
- (first @ws)
- "Cannot resolve property gozMethod for inferred type js/Foo"))))
+ (is (some-> @ws first
+ (string/starts-with?
+ "Cannot resolve property gozMethod for inferred type js/Foo")))))
+
+(comment
+
+ (require '[clojure.java.io :as io]
+ '[cljs.closure :as cc])
+
+ (def externs
+ (-> (cc/js-source-file nil (io/file "src/test/externs/test.js"))
+ externs/parse-externs externs/index-externs))
+
+ (ana/resolve-extern '[Foo gozMethod] externs)
+
+ (clojure.test/test-vars [#'test-type-hint-infer-unknown-method])
+
+ )
(deftest test-infer-unknown-method-from-externs
(let [ws (atom [])
@@ -197,9 +325,9 @@
:warnings ws})]
(is (= (unsplit-lines ["Foo.prototype.gozMethod;"]) res))
(is (= 1 (count @ws)))
- (is (string/starts-with?
- (first @ws)
- "Cannot resolve property gozMethod for inferred type js/Foo"))))
+ (is (some-> @ws first
+ (string/starts-with?
+ "Cannot resolve property gozMethod for inferred type js/Foo")))))
(deftest test-infer-js-require
(let [ws (atom [])
@@ -211,9 +339,9 @@
:warnings ws})]
(is (= (unsplit-lines ["var require;" "Object.Component;"]) res))
(is (= 1 (count @ws)))
- (is (string/starts-with?
- (first @ws)
- "Adding extern to Object for property Component"))))
+ (is (some-> @ws first
+ (string/starts-with?
+ "Adding extern to Object for property Component")))))
(deftest test-set-warn-on-infer
(let [ws (atom [])
@@ -227,7 +355,9 @@
:warn false
:with-core? true})]
(is (= 1 (count @ws)))
- (is (string/starts-with? (first @ws) "Cannot infer target type"))))
+ (is (some-> @ws first
+ (string/starts-with?
+ "Cannot infer target type")))))
(deftest test-cljs-1970-infer-with-cljs-literals
(let [ws (atom [])
diff --git a/src/test/clojure/cljs/externs_parsing_tests.clj b/src/test/clojure/cljs/externs_parsing_tests.clj
index ed0cfdb709..e5a399c84f 100644
--- a/src/test/clojure/cljs/externs_parsing_tests.clj
+++ b/src/test/clojure/cljs/externs_parsing_tests.clj
@@ -37,6 +37,12 @@
(is (= 'any (get-in ns [:defs 'get :ret-tag])))
(is (= 'array (get-in ns [:defs 'getKeys :ret-tag])))))
+(comment
+ ;; works
+ (get-in (externs/analyze-goog-file "goog/object/object.js")
+ [:defs 'containsKey :ret-tag])
+ )
+
(deftest test-parse-super
(let [info (->
(filter
diff --git a/src/test/clojure/cljs/type_inference_tests.clj b/src/test/clojure/cljs/type_inference_tests.clj
index c9c5f63434..1a9170020d 100644
--- a/src/test/clojure/cljs/type_inference_tests.clj
+++ b/src/test/clojure/cljs/type_inference_tests.clj
@@ -307,12 +307,32 @@
(is (= (env/with-compiler-env test-cenv
(:tag (analyze test-env '(dissoc {:foo :bar} :foo))))
'#{clj clj-nil}))
+ (is (= (env/with-compiler-env test-cenv
+ (:tag (analyze test-env '(distinct? 1))))
+ 'boolean))
+ (is (= (env/with-compiler-env test-cenv
+ (:tag (analyze test-env '(special-symbol? 'foo))))
+ 'boolean))
+ ;; TODO: we can't infer isa?, we get 'any which is a bit surprising
+ ;(is (= (env/with-compiler-env test-cenv
+ ; (:tag (analyze test-env '(isa? ::foo :bar))))
+ ; 'boolean))
;; has changed, why does this return #{clj any} ?
;(is (= (env/with-compiler-env test-cenv
; (:tag (analyze test-env '(assoc nil :foo :bar))))
; 'clj))
)
+(deftest lib-inference-extern-call
+ (testing "Test return type inference for core fns whose
+ internal implementation uses standard JS APIs"
+ (is (= 'boolean
+ (env/with-compiler-env test-cenv
+ (:tag (analyze test-env '(array? (array)))))))
+ (is (= 'array
+ (env/with-compiler-env test-cenv
+ (:tag (analyze test-env '(make-array js/String. 10))))))))
+
(deftest test-always-true-if
(is (= (env/with-compiler-env test-cenv
(:tag (analyze test-env '(if 1 2 "foo"))))
@@ -330,10 +350,12 @@
(cljs.env/with-compiler-env test-cenv
(:tag (analyze test-env '(dec x)))))
'number))
- (is (= (ana/no-warn
- (cljs.env/with-compiler-env test-cenv
- (:tag (analyze test-env '(int x)))))
- 'number))
+ ;; we relaxed int, making it too strict just creates
+ ;; problems for legit coercion, Clojure is vague so we are vague :)
+ ;; (is (= (ana/no-warn
+ ;; (cljs.env/with-compiler-env test-cenv
+ ;; (:tag (analyze test-env '(int x)))))
+ ;; 'number))
(is (= (ana/no-warn
(cljs.env/with-compiler-env test-cenv
(:tag (analyze test-env '(unchecked-int x)))))
@@ -374,3 +396,20 @@
(:import [goog.history Html5History]))
(Html5History.)]
{} true))))))
+
+(deftest test-goog-infer
+ (is (= 'boolean
+ (:tag (env/with-compiler-env (env/default-compiler-env)
+ (ana/analyze-form-seq
+ '[(ns test.foo
+ (:require [goog.string :as gstring]))
+ (gstring/contains "foobar" "foo")]
+ {} true)))))
+ (is (= 'boolean
+ (:tag
+ (env/with-compiler-env (env/default-compiler-env)
+ (ana/analyze-form-seq
+ '[(ns test.foo
+ (:require [goog.object :as gobject]))
+ (gobject/containsKey (js-object) "foo")]
+ {} true))))))